In this notebook, we specifically compare using Alevin-fry with cellranger to quantify both single-cell and single-nuclei RNA seq data.
For cellranger, we are using the default parameters for single-cell RNA-seq and the --include-introns option for the single-nuclei RNA-seq option, which has been labeled as the splici index for easy comparison with alevin-fry. For Alevin-fry, we are interested in a few different parameters specifically:
- The use of the splici index vs. the transcriptome index only for single-cell RNA-seq samples
- Use of the cr-like or full resolution
- Use of pseudoalignment (sketch) or selective alignment (salign)
Previously, we had found that the sketch performed well, although there was a slight increase in UMIs/cell and genes detected/cell. Data has also surfaced from Dobin et al. in STARsolo: accurate, fast and versatile mapping/quantification of single-cell and single-nucleus RNA-seq data and in an alevin-fry tutorial indicating pseudoaligners have a tendency to result in false detection of increased gene expression. Use of the splici index with alevin-fry has been reported to decrease this false positive expression.
More about use of the splici index and different resolutions can be found in the pre-print on Alevin-fry.
We will be testing the following conditions of alevin-fry:
- spliced (cDNA) txome, salign, full
- spliced (cDNA) txome, sketch, full
- spliced (cDNA) txome, salign, cr-like
- spliced (cDNA) txome, sketch, cr-like
- unspliced (splici) txome, salign, full
- unspliced (splici) txome, sketch, full
- unspliced (splici) txome, salign, cr-like
- unspliced (splici) txome, sketch, cr-like
There are three single-cell samples (SCPCR000006, SCPCR000126, SCPCR000127) and four single-nuclei samples that were used for comparisons (SCPCR000118, SCPCR000119, SCPCR000220, SCPCR000221).
Setup
library(magrittr)
library(ggplot2)
library(SingleCellExperiment)
Loading required package: SummarizedExperiment
Loading required package: MatrixGenerics
Loading required package: matrixStats
Attaching package: ‘MatrixGenerics’
The following objects are masked from ‘package:matrixStats’:
colAlls, colAnyNAs, colAnys, colAvgsPerRowSet, colCollapse, colCounts, colCummaxs, colCummins,
colCumprods, colCumsums, colDiffs, colIQRDiffs, colIQRs, colLogSumExps, colMadDiffs, colMads,
colMaxs, colMeans2, colMedians, colMins, colOrderStats, colProds, colQuantiles, colRanges, colRanks,
colSdDiffs, colSds, colSums2, colTabulates, colVarDiffs, colVars, colWeightedMads, colWeightedMeans,
colWeightedMedians, colWeightedSds, colWeightedVars, rowAlls, rowAnyNAs, rowAnys, rowAvgsPerColSet,
rowCollapse, rowCounts, rowCummaxs, rowCummins, rowCumprods, rowCumsums, rowDiffs, rowIQRDiffs,
rowIQRs, rowLogSumExps, rowMadDiffs, rowMads, rowMaxs, rowMeans2, rowMedians, rowMins,
rowOrderStats, rowProds, rowQuantiles, rowRanges, rowRanks, rowSdDiffs, rowSds, rowSums2,
rowTabulates, rowVarDiffs, rowVars, rowWeightedMads, rowWeightedMeans, rowWeightedMedians,
rowWeightedSds, rowWeightedVars
Loading required package: GenomicRanges
Loading required package: stats4
Loading required package: BiocGenerics
Loading required package: parallel
Attaching package: ‘BiocGenerics’
The following objects are masked from ‘package:parallel’:
clusterApply, clusterApplyLB, clusterCall, clusterEvalQ, clusterExport, clusterMap, parApply,
parCapply, parLapply, parLapplyLB, parRapply, parSapply, parSapplyLB
The following objects are masked from ‘package:stats’:
IQR, mad, sd, var, xtabs
The following objects are masked from ‘package:base’:
anyDuplicated, append, as.data.frame, basename, cbind, colnames, dirname, do.call, duplicated, eval,
evalq, Filter, Find, get, grep, grepl, intersect, is.unsorted, lapply, Map, mapply, match, mget,
order, paste, pmax, pmax.int, pmin, pmin.int, Position, rank, rbind, Reduce, rownames, sapply,
setdiff, sort, table, tapply, union, unique, unsplit, which.max, which.min
Loading required package: S4Vectors
Attaching package: ‘S4Vectors’
The following object is masked from ‘package:base’:
expand.grid
Loading required package: IRanges
Loading required package: GenomeInfoDb
Loading required package: Biobase
Welcome to Bioconductor
Vignettes contain introductory material; view with 'browseVignettes()'. To cite Bioconductor, see
'citation("Biobase")', and for packages 'citation("pkgname")'.
Attaching package: ‘Biobase’
The following object is masked from ‘package:MatrixGenerics’:
rowMedians
The following objects are masked from ‘package:matrixStats’:
anyMissing, rowMedians
function_path <- file.path("..", "benchmarking-functions", "R")
miceadds::source.all(function_path)
*** source aws_copy_samples.R
*** source make_sce_list.R
*** source quant_info_table.R
*** source sce_to_df.R
base_dir <- here::here()
file_dir <- file.path(base_dir, "results")
# sce files
cDNA_salign_full_file <- file.path(file_dir, "cDNA_salign_full_sces.rds")
cDNA_sketch_full_file <- file.path(file_dir, "cDNA_sketch_full_sces.rds")
cDNA_salign_cr_file <- file.path(file_dir, "cDNA_salign_cr_sces.rds")
cDNA_sketch_cr_file <- file.path(file_dir, "cDNA_sketch_cr_sces.rds")
splici_salign_full_file <- file.path(file_dir, "splici_salign_full_sces.rds")
splici_sketch_full_file <- file.path(file_dir, "splici_sketch_full_sces.rds")
splici_salign_cr_file <- file.path(file_dir, "splici_salign_cr_sces.rds")
splici_sketch_cr_file <- file.path(file_dir, "splici_sketch_cr_sces.rds")
cellranger_file <- file.path(file_dir, "cellranger_sces.rds")
# qc files
quant_info_file <- file.path(file_dir, "quant_info.tsv")
coldata_df_file <- file.path(file_dir, "coldata_qc.tsv")
rowdata_df_file <- file.path(file_dir, "rowdata_qc.tsv")
# mito gene list
mito_file <- file.path(base_dir, "sample-info", "Homo_sapiens.GRCh38.103.mitogenes.txt")
# read in sces
cDNA_salign_full <- readr::read_rds(cDNA_salign_full_file)
cDNA_sketch_full <- readr::read_rds(cDNA_sketch_full_file)
cDNA_salign_cr <- readr::read_rds(cDNA_salign_cr_file)
cDNA_sketch_cr <- readr::read_rds(cDNA_sketch_cr_file)
splici_salign_full <- readr::read_rds(splici_salign_full_file)
splici_sketch_full <- readr::read_rds(splici_sketch_full_file)
splici_salign_cr <- readr::read_rds(splici_salign_cr_file)
splici_sketch_cr <- readr::read_rds(splici_sketch_cr_file)
cellranger <- readr::read_rds(cellranger_file)
sce_list <- list(
cDNA_salign_full,
cDNA_sketch_full,
cDNA_salign_cr,
cDNA_sketch_cr,
splici_salign_full,
splici_sketch_full,
splici_salign_cr,
splici_sketch_cr,
cellranger
)
names(sce_list) <- c(
'cDNA_salign_full',
'cDNA_sketch_full',
'cDNA_salign_cr',
'cDNA_sketch_cr',
'splici_salign_full',
'splici_sketch_full',
'splici_salign_cr',
'splici_sketch_cr',
'cellranger'
)
quant_info <- readr::read_tsv(quant_info_file)
── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────
cols(
tool = col_character(),
quant_dir = col_character(),
sample = col_character(),
index_type = col_character(),
alevin_alignment = col_character(),
alevin_resolution = col_character(),
filter_strategy = col_character(),
data_dir = col_character(),
filter = col_logical(),
usa_mode = col_logical(),
intron_mode = col_logical(),
seq_unit = col_character(),
which_counts = col_character()
)
coldata_df <- readr::read_tsv(coldata_df_file)
── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────
cols(
quant_id = col_character(),
cell_id = col_character(),
sum = col_double(),
detected = col_double(),
subsets_mito_sum = col_double(),
subsets_mito_detected = col_double(),
subsets_mito_percent = col_double(),
total = col_double(),
cells_detected = col_double(),
tool = col_character()
)
rowdata_df <- readr::read_tsv(rowdata_df_file)
── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────
cols(
quant_id = col_character(),
gene_id = col_character(),
mean = col_double(),
detected = col_double(),
tool = col_character(),
ID = col_logical(),
Symbol = col_logical(),
Type = col_logical()
)
539190 parsing failures.
row col expected actual file
2296753 ID 1/0/T/F/TRUE/FALSE ENSG00000186092 '/Users/allyhawkins/Documents/ALSF/git_repos/alsf-scpca/results/rowdata_qc.tsv'
2296753 Symbol 1/0/T/F/TRUE/FALSE OR4F5 '/Users/allyhawkins/Documents/ALSF/git_repos/alsf-scpca/results/rowdata_qc.tsv'
2296753 Type 1/0/T/F/TRUE/FALSE Gene Expression '/Users/allyhawkins/Documents/ALSF/git_repos/alsf-scpca/results/rowdata_qc.tsv'
2296754 ID 1/0/T/F/TRUE/FALSE ENSG00000284733 '/Users/allyhawkins/Documents/ALSF/git_repos/alsf-scpca/results/rowdata_qc.tsv'
2296754 Symbol 1/0/T/F/TRUE/FALSE OR4F29 '/Users/allyhawkins/Documents/ALSF/git_repos/alsf-scpca/results/rowdata_qc.tsv'
....... ...... .................. ............... ...............................................................................
See problems(...) for more details.
mito_genes <- readr::read_tsv(mito_file, col_names = "gene_id")
── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────
cols(
gene_id = col_character()
)
mito_genes <- mito_genes %>%
dplyr::pull(gene_id) %>%
unique()
## fix error for 220 and 221 run with cellranger that should have index_type as splici
quant_info[which(quant_info$quant_dir == "SCPCR000220-cdna-pre_mRNA"),"index_type"] <- "splici"
quant_info[which(quant_info$quant_dir == "SCPCR000221-cdna-pre_mRNA"),"index_type"] <- "splici"
# merge coldata df with quant_info
coldata_info_df <- coldata_df %>%
dplyr::mutate(tool = dplyr::case_when(tool == "alevin-fry-unfiltered" ~ "alevin-fry",
tool == "cellranger" ~ "cellranger")) %>%
dplyr::left_join(quant_info,
by = c("tool" = "tool",
"quant_id" = "quant_dir"))
Comparison of QC Metrics
Mitochondrial Content
First, we will start by just looking at mitochondrial content across each of the tools.
ggplot(coldata_info_df, aes(x = tool, y = subsets_mito_percent, fill = tool)) +
geom_boxplot() +
facet_grid(sample ~ index_type) +
theme_classic() +
ylab("% Mito /Cell") +
theme(axis.ticks.x = element_blank(), axis.text.x = element_text(angle = 45, hjust = 1))

It looks like generally mitochondrial content is uniform and low across all tools and all samples, which is great.
Before doing anymore plotting, let’s split our dataframe by single-cell and single-nucleus RNA-seq samples.
cell_coldata_qc <- coldata_info_df %>%
dplyr::filter(seq_unit == "cell")
nucleus_coldata_qc <- coldata_info_df %>%
dplyr::filter(seq_unit == "nucleus") %>%
dplyr::filter(index_type == "splici")
Per Cell QC Metrics in all cells
We are going to look at some QC metrics at a per cell level. Specifically we will look at UMI/cell and genes detected/cell.
ggplot(nucleus_coldata_qc, aes(x = alevin_resolution, y = sum, fill = tool)) +
geom_boxplot() +
facet_grid(~ sample) +
theme_classic() +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
ylab("UMI/cell") +
xlab("") +
coord_cartesian(ylim = c(0,30000))

ggplot(cell_coldata_qc, aes(x = alevin_resolution, y = sum, fill = tool)) +
geom_boxplot() +
facet_grid(sample ~ index_type) +
theme_classic() +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
ylab("UMI/cell") +
xlab("") +
ylim(c(0,50000))

In looking at UMI/cell between cell and nucleus samples, it’s very clear to me that the single-nuclei samples have a bit more fluctuation - both across samples and across tools. The single-cell samples tend to be fairly uniform across all tools, although it looks like the median in 126 and 127 is lower in alevin-fry than in cellranger. This is not the case in the single-nuclei samples where we see that alevin-fry doesn’t seem to be capturing as many counts, specifically in sample 220.

Here, we see even more variation in genes detected per cell in the single-nuclei samples with cellranger tending to have much tighter IQR’s than alevin-fry. However, we are still looking at all cells and not just shared cells only, so that could be part of the issue. Alevin-fry could be detecting more cells with lower counts than cellranger.

Per Cell QC Metrics in Shared Cells only, No Minimum Gene Coverage
Now let’s look at the same metrics but in shared cells only to see if any of these fluctuations across tools are because different cells are being identified.

nuclei_counts <- nucleus_coldata_qc %>%
dplyr::count(cell_id, sample)
common_nuclei <- nuclei_counts %>%
dplyr::filter(n == 5) %>%
dplyr::pull(cell_id)
nucleus_qc_common <- nucleus_coldata_qc %>%
dplyr::filter(
(cell_id %in% common_nuclei)
)
Does mitochondrial content change when we only look at shared data? I expect not since it was already pretty uniform.
# mito comparison across shared cells only of all runs
# nucleus samples first
ggplot(nucleus_qc_common, aes(x = alevin_resolution, y = subsets_mito_percent, fill = tool)) +
geom_boxplot() +
facet_grid(~ sample) +
theme_classic() +
ylab("% Mito /Cell") +
theme(axis.ticks.x = element_blank(), axis.text.x = element_blank())

# single cell
ggplot(cell_qc_common, aes(x = alevin_resolution, y = subsets_mito_percent, fill = tool)) +
geom_boxplot() +
facet_grid(sample ~ index_type) +
theme_classic() +
ylab("% Mito /Cell") +
theme(axis.ticks.x = element_blank(), axis.text.x = element_blank())

The answer is no, it seems to still be quite similar, although here I am plotting it by breaking out the single-nuclei samples and you can see that SCPCR000118 has higher mito content than the other samples and fluctuates across tools. In the previous benchmarking I was worried about this sample not being high quality and although technically the mito content is still below 20%, it doesn’t look as uniform as in 220 or 221.
Let’s look at UMI/cell and genes detected/cell in the shared cells.
ggplot(nucleus_qc_common, aes(x = alevin_resolution, y = sum, fill = tool)) +
geom_boxplot() +
facet_grid(~ sample) +
theme_classic() +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
ylab("UMI/cell") +
xlab("") +
coord_cartesian(ylim = c(0,30000))

ggplot(cell_qc_common, aes(x = alevin_resolution, y = sum, fill = tool)) +
geom_boxplot() +
facet_grid(sample ~ index_type) +
theme_classic() +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
ylab("UMI/cell") +
xlab("") +
ylim(c(0,50000))


ggplot(cell_qc_common, aes(x = alevin_resolution, y = detected, fill = tool)) +
geom_boxplot() +
facet_grid(~ sample) +
theme_classic() +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
ylab("Genes detected/cell") +
xlab("") +
coord_cartesian(ylim = c(0,10000))

Overall, it looks like we have eliminated many of the differences between the tools in the single-nuclei RNA-seq data. Although it appears that across the board the cr-like resolution gives more on par distributions to cellranger than the full resolution. The full resolution seems to have higher UMIs/cell and genes/cell across most samples. I’m not entirely sure how to interpret that at this point.
It does look like alevin-fry is detecting more cells than cellranger, so perhaps there is something we could be doing at the filtering stage that could help improve this?
Let’s take a quick look at the number of cells detected by tool just to be sure that this hypothesis is correct.
cell_numbers <- coldata_info_df %>%
dplyr::group_by(quant_id, tool, sample) %>%
#dplyr::filter(sample != "SCPCR000118") %>%
dplyr::tally()
ggplot(cell_numbers, aes(x = sample, y = n, color = tool)) +
geom_point() +
theme_classic()

As expected, for all samples (except SCPCR000006), cellranger is detecting less cells than in alevin-fry. I’m also not sure what has happened with SCPCR000118, but we saw this previously and it also shows strange patterns in mitochondrial content and very low coverage across the board.
Per Cell QC Metrics in Shared Cells, Minimum Gene Coverage
To see if the increase in genes detected/cell in the full resolution could be due to low covered genes, let’s look at the number of genes detected if we were to remove genes only found in < 5% of cells.
for(list in sce_list){
list <- list %>%
purrr::map(addCellQC, mito = mito_genes, threshold = 5)
}
Loading required package: HDF5Array
Loading required package: DelayedArray
Loading required package: Matrix
Attaching package: ‘Matrix’
The following object is masked from ‘package:S4Vectors’:
expand
Attaching package: ‘DelayedArray’
The following objects are masked from ‘package:base’:
aperm, apply, rowsum
Loading required package: rhdf5
# merge back into a dataframe for plotting
df_list <- list()
i = 1
for(list in sce_list){
df_list[[i]] <- list %>%
purrr::map_df(coldata_to_df, .id = "quant_id")
i = i + 1
}
# change names of the sce list to be the tool used first
names(df_list) <- c(rep("alevin-fry", 8), "cellranger")
coldata_threshold <- df_list %>%
dplyr::bind_rows(.id = "tool")
coldata_info_threshold_df <- coldata_threshold %>%
dplyr::left_join(quant_info,
by = c("tool" = "tool",
"quant_id" = "quant_dir"))
cell_coldata_threshold_qc <- coldata_info_threshold_df %>%
dplyr::filter(seq_unit == "cell")
nucleus_coldata_threshold_qc <- coldata_info_threshold_df %>%
dplyr::filter(seq_unit == "nucleus" & index_type == "splici")
cell_counts_threshold <- cell_coldata_threshold_qc %>%
dplyr::count(cell_id, sample)
common_cells_threshold <- cell_counts_threshold %>%
dplyr::filter(n == 9) %>%
dplyr::pull(cell_id)
cell_qc_common_threshold <- cell_coldata_threshold_qc %>%
dplyr::filter(
(cell_id %in% common_cells_threshold)
)
nuclei_counts_threshold <- nucleus_coldata_threshold_qc %>%
dplyr::count(cell_id, sample)
common_nuclei_threshold <- nuclei_counts_threshold %>%
dplyr::filter(n == 5) %>%
dplyr::pull(cell_id)
nucleus_qc_common_threshold <- nucleus_coldata_threshold_qc %>%
dplyr::filter(
(cell_id %in% common_nuclei_threshold)
)
ggplot(nucleus_qc_common_threshold, aes(x = alevin_resolution, y = detected, fill = tool)) +
geom_boxplot() +
facet_grid(~ sample) +
theme_classic() +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
ylab("Genes detected/cell") +
xlab("") +
coord_cartesian(ylim = c(0,15000))


It does not appear that the increase in genes detected observed in the full resolution is resolved by removing lowly covered genes.
Per Gene QC Metrics
Next, we will look at some metrics comparing mean gene expression across genes identified for each sample using each tool. To do that, we will first filter by only those genes that are detected in more than 5% of cells and shared across all tool configurations.
# spread table for comparisons
cell_rowdata_cor <- cell_rowdata_common %>%
dplyr::select(tool, index_type, alevin_resolution, alevin_alignment, gene_id, sample, sum) %>%
# spread the mean expression stats to one column per caller
tidyr::pivot_wider(id_cols = c(gene_id, sample),
names_from = c("tool", "index_type", "alevin_resolution", "alevin_alignment"),
values_from = sum) %>%
# drop rows with NA values to ease correlation calculations
tidyr::drop_na()
Error: Can't subset columns that don't exist.
x Column `sum` doesn't exist.
Run `rlang::last_error()` to see where the error occurred.
Now we can look at the correlation of mean gene expression across each of our tool configurations in each sample.
nucleus_rowdata_cor %>%
dplyr::group_by(sample) %>%
dplyr::summarize(
splici_salign_full_cor = cor(`cellranger_splici_not_alevin_not_alevin`, `alevin-fry_splici_full_salign`, method = "spearman"),
splici_sketch_full_cor = cor(`cellranger_splici_not_alevin_not_alevin`, `alevin-fry_splici_full_sketch`, method = "spearman"),
splici_salign_cr_cor = cor(`cellranger_splici_not_alevin_not_alevin`, `alevin-fry_splici_cr_salign`, method = "spearman"),
splici_sketch_cr_cor = cor(`cellranger_splici_not_alevin_not_alevin`, `alevin-fry_splici_cr_sketch`, method = "spearman")
)
Just like with UMIs/cell, these correlations are quite high across all of the tools and all of the samples. The biggest drop in correlation does seem to be in the splici_salign_full and splici_sketch_full vs. cellranger comparisons in the single-cell samples with only around 0.95-0.97 in correlation coefficients.
We can look at a few examples more closely to see how well the mean gene expression for each gene actually lines up.
ggplot(cell_rowdata_cor, aes(x = `cellranger_cDNA_not_alevin_not_alevin`, y = `alevin-fry_cDNA_full_sketch`)) +
geom_point(size = 0.5, alpha = 0.1) +
facet_wrap(~ sample) +
scale_x_log10() +
scale_y_log10() +
labs(x = "Cell Ranger mean gene expression", y = "Alevin Fry, cDNA index, Full Resolution Sketch Mean gene expression") +
theme_classic()




Interestingly, when you look at the cDNA index, you do see genes with higher expression found in Alevin-fry in compared to cellranger (shown by a group to the upper left of the diagonal), but when you move to the splici index that group disappears. With the splici index and full resolution there still appears to be some increase in gene expression detected in Alevin-fry, but with the cr-like resolution it almost looks like there is now lower detection in gene expression and some genes have lost gene expression (shown with a group to the right of the diagonal in the last plot).
Let’s look at the single-nuclei data.


Here, we see a similar trend where with the full resolution there are genes with increased expression in Alevin-fry, while with the cr-like it seems more centered around the diagonal with some genes having increased expression and some having lower expression than in cellranger.
Some closing thoughts:
Should we be exploring some different filtering options with Alevin-fry? It looks like Alevin-fry is doing just as good of a job as cellranger, but specifically with the single-nuclei data, we see more cells with low counts that are in the final counts matrix - maybe this isn’t a problem and low count cells would get removed ideally before any downstream analysis anyways.
Splici with single-cell samples seems to be performing similarly to the other Alevin-fry modes and cellranger - it even looks like it does decrease some gene expression in genes that are poorly correlated between Alevin-fry and cellranger.
Cr-like gives similar results to cellranger, more so than the full resolution, although it does look like we could be leading to some lower gene expression in the single-cell samples with Alevin-fry. I’m not sure I can confidently make a decision here on which one would be the appropriate choice yet (although I can say I think both seem to do well).
LS0tCnRpdGxlOiAiQWxldmluLUZyeSB2cy4gQ2VsbHJhbmdlciBDb21wYXJpc29uIgphdXRob3I6ICJBbGx5IEhhd2tpbnMgZm9yIENDREwiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCi0tLQoKSW4gdGhpcyBub3RlYm9vaywgd2Ugc3BlY2lmaWNhbGx5IGNvbXBhcmUgdXNpbmcgQWxldmluLWZyeSB3aXRoIGNlbGxyYW5nZXIgdG8gcXVhbnRpZnkgYm90aCBzaW5nbGUtY2VsbCBhbmQgc2luZ2xlLW51Y2xlaSBSTkEgc2VxIGRhdGEuIAoKRm9yIGNlbGxyYW5nZXIsIHdlIGFyZSB1c2luZyB0aGUgZGVmYXVsdCBwYXJhbWV0ZXJzIGZvciBzaW5nbGUtY2VsbCBSTkEtc2VxIGFuZCB0aGUgYC0taW5jbHVkZS1pbnRyb25zYCBvcHRpb24gZm9yIHRoZSBzaW5nbGUtbnVjbGVpIFJOQS1zZXEgb3B0aW9uLCB3aGljaCBoYXMgYmVlbiBsYWJlbGVkIGFzIHRoZSBgc3BsaWNpYCBpbmRleCBmb3IgZWFzeSBjb21wYXJpc29uIHdpdGggYWxldmluLWZyeS4gCkZvciBBbGV2aW4tZnJ5LCB3ZSBhcmUgaW50ZXJlc3RlZCBpbiBhIGZldyBkaWZmZXJlbnQgcGFyYW1ldGVycyBzcGVjaWZpY2FsbHk6IAoKLSBUaGUgdXNlIG9mIHRoZSBzcGxpY2kgaW5kZXggdnMuIHRoZSB0cmFuc2NyaXB0b21lIGluZGV4IG9ubHkgZm9yIHNpbmdsZS1jZWxsIFJOQS1zZXEgc2FtcGxlcyAKLSBVc2Ugb2YgdGhlIGNyLWxpa2Ugb3IgZnVsbCByZXNvbHV0aW9uIAotIFVzZSBvZiBwc2V1ZG9hbGlnbm1lbnQgKHNrZXRjaCkgb3Igc2VsZWN0aXZlIGFsaWdubWVudCAoc2FsaWduKQoKUHJldmlvdXNseSwgd2UgaGFkIGZvdW5kIHRoYXQgdGhlIHNrZXRjaCBwZXJmb3JtZWQgd2VsbCwgYWx0aG91Z2ggdGhlcmUgd2FzIGEgc2xpZ2h0IGluY3JlYXNlIGluIFVNSXMvY2VsbCBhbmQgZ2VuZXMgZGV0ZWN0ZWQvY2VsbC4gCkRhdGEgaGFzIGFsc28gc3VyZmFjZWQgZnJvbSBEb2JpbiBfZXQgYWwuXyBpbiBbU1RBUnNvbG86IGFjY3VyYXRlLCBmYXN0IGFuZCB2ZXJzYXRpbGUgbWFwcGluZy9xdWFudGlmaWNhdGlvbiBvZiBzaW5nbGUtY2VsbCBhbmQgc2luZ2xlLW51Y2xldXMgUk5BLXNlcSBkYXRhXShodHRwczovL3d3dy5iaW9yeGl2Lm9yZy9jb250ZW50LzEwLjExMDEvMjAyMS4wNS4wNS40NDI3NTV2MSkgYW5kIGluIGFuIFthbGV2aW4tZnJ5IHR1dG9yaWFsXShodHRwczovL2NvbWJpbmUtbGFiLmdpdGh1Yi5pby9hbGV2aW4tZnJ5LXR1dG9yaWFscy8yMDIxL2ltcHJvdmluZy10eG9tZS1zcGVjaWZpY2l0eS8pIGluZGljYXRpbmcgcHNldWRvYWxpZ25lcnMgaGF2ZSBhIHRlbmRlbmN5IHRvIHJlc3VsdCBpbiBmYWxzZSBkZXRlY3Rpb24gb2YgaW5jcmVhc2VkIGdlbmUgZXhwcmVzc2lvbi4gClVzZSBvZiB0aGUgYHNwbGljaWAgaW5kZXggd2l0aCBhbGV2aW4tZnJ5IGhhcyBiZWVuIHJlcG9ydGVkIHRvIGRlY3JlYXNlIHRoaXMgZmFsc2UgcG9zaXRpdmUgZXhwcmVzc2lvbi4gCgpNb3JlIGFib3V0IHVzZSBvZiB0aGUgYHNwbGljaWAgaW5kZXggYW5kIGRpZmZlcmVudCByZXNvbHV0aW9ucyBjYW4gYmUgZm91bmQgaW4gdGhlIHByZS1wcmludCBvbiBbQWxldmluLWZyeS5dKGh0dHBzOi8vd3d3LmJpb3J4aXYub3JnL2NvbnRlbnQvMTAuMTEwMS8yMDIxLjA2LjI5LjQ1MDM3N3YxKQoKV2Ugd2lsbCBiZSB0ZXN0aW5nIHRoZSBmb2xsb3dpbmcgY29uZGl0aW9ucyBvZiBhbGV2aW4tZnJ5OiAKCi0gc3BsaWNlZCAoY0ROQSkgdHhvbWUsIHNhbGlnbiwgZnVsbAotIHNwbGljZWQgKGNETkEpIHR4b21lLCBza2V0Y2gsIGZ1bGwKLSBzcGxpY2VkIChjRE5BKSB0eG9tZSwgc2FsaWduLCBjci1saWtlCi0gc3BsaWNlZCAoY0ROQSkgdHhvbWUsIHNrZXRjaCwgY3ItbGlrZQotIHVuc3BsaWNlZCAoc3BsaWNpKSB0eG9tZSwgc2FsaWduLCBmdWxsCi0gdW5zcGxpY2VkIChzcGxpY2kpIHR4b21lLCBza2V0Y2gsIGZ1bGwKLSB1bnNwbGljZWQgKHNwbGljaSkgdHhvbWUsIHNhbGlnbiwgY3ItbGlrZQotIHVuc3BsaWNlZCAoc3BsaWNpKSB0eG9tZSwgc2tldGNoLCBjci1saWtlCgpUaGVyZSBhcmUgdGhyZWUgc2luZ2xlLWNlbGwgc2FtcGxlcyAoU0NQQ1IwMDAwMDYsIFNDUENSMDAwMTI2LCBTQ1BDUjAwMDEyNykgYW5kIGZvdXIgc2luZ2xlLW51Y2xlaSBzYW1wbGVzIHRoYXQgd2VyZSB1c2VkIGZvciBjb21wYXJpc29ucyAoU0NQQ1IwMDAxMTgsIFNDUENSMDAwMTE5LCBTQ1BDUjAwMDIyMCwgU0NQQ1IwMDAyMjEpLiAKCiMjIFNldHVwIAoKYGBge3J9CmxpYnJhcnkobWFncml0dHIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShTaW5nbGVDZWxsRXhwZXJpbWVudCkKYGBgCgoKYGBge3J9CiMgbG9hZCBpbiBiZW5jaG1hcmtpbmcgZnVuY3Rpb25zIApmdW5jdGlvbl9wYXRoIDwtIGZpbGUucGF0aCgiLi4iLCAiYmVuY2htYXJraW5nLWZ1bmN0aW9ucyIsICJSIikKbWljZWFkZHM6OnNvdXJjZS5hbGwoZnVuY3Rpb25fcGF0aCkKYGBgCgoKYGBge3J9CiMgcGF0aCB0byByZXN1bHRzIGZpbGVzIHdpdGggc2NlcyBhbmQgcWMgZGF0YWZyYW1lcyAKYmFzZV9kaXIgPC0gaGVyZTo6aGVyZSgpCmZpbGVfZGlyIDwtIGZpbGUucGF0aChiYXNlX2RpciwgInJlc3VsdHMiKQoKIyBzY2UgZmlsZXMgCmNETkFfc2FsaWduX2Z1bGxfZmlsZSA8LSBmaWxlLnBhdGgoZmlsZV9kaXIsICJjRE5BX3NhbGlnbl9mdWxsX3NjZXMucmRzIikKY0ROQV9za2V0Y2hfZnVsbF9maWxlIDwtIGZpbGUucGF0aChmaWxlX2RpciwgImNETkFfc2tldGNoX2Z1bGxfc2Nlcy5yZHMiKQpjRE5BX3NhbGlnbl9jcl9maWxlIDwtIGZpbGUucGF0aChmaWxlX2RpciwgImNETkFfc2FsaWduX2NyX3NjZXMucmRzIikKY0ROQV9za2V0Y2hfY3JfZmlsZSA8LSBmaWxlLnBhdGgoZmlsZV9kaXIsICJjRE5BX3NrZXRjaF9jcl9zY2VzLnJkcyIpCnNwbGljaV9zYWxpZ25fZnVsbF9maWxlIDwtIGZpbGUucGF0aChmaWxlX2RpciwgInNwbGljaV9zYWxpZ25fZnVsbF9zY2VzLnJkcyIpCnNwbGljaV9za2V0Y2hfZnVsbF9maWxlIDwtIGZpbGUucGF0aChmaWxlX2RpciwgInNwbGljaV9za2V0Y2hfZnVsbF9zY2VzLnJkcyIpCnNwbGljaV9zYWxpZ25fY3JfZmlsZSA8LSBmaWxlLnBhdGgoZmlsZV9kaXIsICJzcGxpY2lfc2FsaWduX2NyX3NjZXMucmRzIikKc3BsaWNpX3NrZXRjaF9jcl9maWxlIDwtIGZpbGUucGF0aChmaWxlX2RpciwgInNwbGljaV9za2V0Y2hfY3Jfc2Nlcy5yZHMiKQpjZWxscmFuZ2VyX2ZpbGUgPC0gZmlsZS5wYXRoKGZpbGVfZGlyLCAiY2VsbHJhbmdlcl9zY2VzLnJkcyIpCgojIHFjIGZpbGVzIAoKcXVhbnRfaW5mb19maWxlIDwtIGZpbGUucGF0aChmaWxlX2RpciwgInF1YW50X2luZm8udHN2IikKY29sZGF0YV9kZl9maWxlIDwtIGZpbGUucGF0aChmaWxlX2RpciwgImNvbGRhdGFfcWMudHN2IikKcm93ZGF0YV9kZl9maWxlIDwtIGZpbGUucGF0aChmaWxlX2RpciwgInJvd2RhdGFfcWMudHN2IikKCiMgbWl0byBnZW5lIGxpc3QgCm1pdG9fZmlsZSA8LSBmaWxlLnBhdGgoYmFzZV9kaXIsICJzYW1wbGUtaW5mbyIsICJIb21vX3NhcGllbnMuR1JDaDM4LjEwMy5taXRvZ2VuZXMudHh0IikKYGBgCgoKYGBge3J9CiMgcmVhZCBpbiBzY2VzCmNETkFfc2FsaWduX2Z1bGwgPC0gcmVhZHI6OnJlYWRfcmRzKGNETkFfc2FsaWduX2Z1bGxfZmlsZSkKY0ROQV9za2V0Y2hfZnVsbCA8LSByZWFkcjo6cmVhZF9yZHMoY0ROQV9za2V0Y2hfZnVsbF9maWxlKQpjRE5BX3NhbGlnbl9jciA8LSByZWFkcjo6cmVhZF9yZHMoY0ROQV9zYWxpZ25fY3JfZmlsZSkKY0ROQV9za2V0Y2hfY3IgPC0gcmVhZHI6OnJlYWRfcmRzKGNETkFfc2tldGNoX2NyX2ZpbGUpCnNwbGljaV9zYWxpZ25fZnVsbCA8LSByZWFkcjo6cmVhZF9yZHMoc3BsaWNpX3NhbGlnbl9mdWxsX2ZpbGUpCnNwbGljaV9za2V0Y2hfZnVsbCA8LSByZWFkcjo6cmVhZF9yZHMoc3BsaWNpX3NrZXRjaF9mdWxsX2ZpbGUpCnNwbGljaV9zYWxpZ25fY3IgPC0gcmVhZHI6OnJlYWRfcmRzKHNwbGljaV9zYWxpZ25fY3JfZmlsZSkKc3BsaWNpX3NrZXRjaF9jciA8LSByZWFkcjo6cmVhZF9yZHMoc3BsaWNpX3NrZXRjaF9jcl9maWxlKQpjZWxscmFuZ2VyIDwtIHJlYWRyOjpyZWFkX3JkcyhjZWxscmFuZ2VyX2ZpbGUpCgojIG1ha2UgYSBsaXN0IHRoYXQgd2lsbCBiZSB1c2VkIGxhdGVyIGZvciBjYWxjdWxhdGluZyBxYyB3aXRoIGEgc3BlY2lmaWMgdGhyZXNob2xkCnNjZV9saXN0IDwtIGxpc3QoCiAgY0ROQV9zYWxpZ25fZnVsbCwKICBjRE5BX3NrZXRjaF9mdWxsLAogIGNETkFfc2FsaWduX2NyLAogIGNETkFfc2tldGNoX2NyLAogIHNwbGljaV9zYWxpZ25fZnVsbCwKICBzcGxpY2lfc2tldGNoX2Z1bGwsCiAgc3BsaWNpX3NhbGlnbl9jciwKICBzcGxpY2lfc2tldGNoX2NyLAogIGNlbGxyYW5nZXIKKQoKbmFtZXMoc2NlX2xpc3QpIDwtIGMoCiAgJ2NETkFfc2FsaWduX2Z1bGwnLAogICdjRE5BX3NrZXRjaF9mdWxsJywKICAnY0ROQV9zYWxpZ25fY3InLAogICdjRE5BX3NrZXRjaF9jcicsCiAgJ3NwbGljaV9zYWxpZ25fZnVsbCcsCiAgJ3NwbGljaV9za2V0Y2hfZnVsbCcsCiAgJ3NwbGljaV9zYWxpZ25fY3InLAogICdzcGxpY2lfc2tldGNoX2NyJywKICAnY2VsbHJhbmdlcicKKQpgYGAKCgpgYGB7cn0KIyByZWFkIGluIGRhdGFmcmFtZXMgbmVlZGVkIGZvciBwbG90dGluZwpxdWFudF9pbmZvIDwtIHJlYWRyOjpyZWFkX3RzdihxdWFudF9pbmZvX2ZpbGUpCmNvbGRhdGFfZGYgPC0gcmVhZHI6OnJlYWRfdHN2KGNvbGRhdGFfZGZfZmlsZSkKcm93ZGF0YV9kZiA8LSByZWFkcjo6cmVhZF90c3Yocm93ZGF0YV9kZl9maWxlKQpgYGAKCgpgYGB7cn0KIyBsb2FkIGluIG1pdG8gZ2VuZXMgZmlsZSB1c2VkIGxhdGVyIAptaXRvX2dlbmVzIDwtIHJlYWRyOjpyZWFkX3RzdihtaXRvX2ZpbGUsIGNvbF9uYW1lcyA9ICJnZW5lX2lkIikKbWl0b19nZW5lcyA8LSBtaXRvX2dlbmVzICU+JQogIGRwbHlyOjpwdWxsKGdlbmVfaWQpICU+JQogIHVuaXF1ZSgpCmBgYAoKYGBge3J9CiMgcHJlcCB0aGUgZGF0YWZyYW1lcyBmb3IgcGxvdHRpbmcKIyMgZml4IGVycm9yIGZvciAyMjAgYW5kIDIyMSBydW4gd2l0aCBjZWxscmFuZ2VyIHRoYXQgc2hvdWxkIGhhdmUgaW5kZXhfdHlwZSBhcyBzcGxpY2kgCnF1YW50X2luZm9bd2hpY2gocXVhbnRfaW5mbyRxdWFudF9kaXIgPT0gIlNDUENSMDAwMjIwLWNkbmEtcHJlX21STkEiKSwiaW5kZXhfdHlwZSJdIDwtICJzcGxpY2kiCnF1YW50X2luZm9bd2hpY2gocXVhbnRfaW5mbyRxdWFudF9kaXIgPT0gIlNDUENSMDAwMjIxLWNkbmEtcHJlX21STkEiKSwiaW5kZXhfdHlwZSJdIDwtICJzcGxpY2kiCgojIG1lcmdlIGNvbGRhdGEgZGYgd2l0aCBxdWFudF9pbmZvCmNvbGRhdGFfaW5mb19kZiA8LSBjb2xkYXRhX2RmICU+JQogIGRwbHlyOjptdXRhdGUodG9vbCA9IGRwbHlyOjpjYXNlX3doZW4odG9vbCA9PSAiYWxldmluLWZyeS11bmZpbHRlcmVkIiB+ICJhbGV2aW4tZnJ5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvb2wgPT0gImNlbGxyYW5nZXIiIH4gImNlbGxyYW5nZXIiKSkgJT4lCiAgZHBseXI6OmxlZnRfam9pbihxdWFudF9pbmZvLAogICAgICAgICAgICAgICAgICAgYnkgPSBjKCJ0b29sIiA9ICJ0b29sIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgInF1YW50X2lkIiA9ICJxdWFudF9kaXIiKSkKYGBgCgojIyBDb21wYXJpc29uIG9mIFFDIE1ldHJpY3MKCiMjIyBNaXRvY2hvbmRyaWFsIENvbnRlbnQKCkZpcnN0LCB3ZSB3aWxsIHN0YXJ0IGJ5IGp1c3QgbG9va2luZyBhdCBtaXRvY2hvbmRyaWFsIGNvbnRlbnQgYWNyb3NzIGVhY2ggb2YgdGhlIHRvb2xzLiAKCgpgYGB7ciBmaWcuaGVpZ2h0ID0gNSwgZmlnLndpZHRoPTV9CmdncGxvdChjb2xkYXRhX2luZm9fZGYsIGFlcyh4ID0gdG9vbCwgeSA9IHN1YnNldHNfbWl0b19wZXJjZW50LCBmaWxsID0gdG9vbCkpICsgCiAgZ2VvbV9ib3hwbG90KCkgKyAKICBmYWNldF9ncmlkKHNhbXBsZSB+IGluZGV4X3R5cGUpICsgCiAgdGhlbWVfY2xhc3NpYygpICsgCiAgeWxhYigiJSBNaXRvIC9DZWxsIikgKyAKICB0aGVtZShheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpCmBgYApJdCBsb29rcyBsaWtlIGdlbmVyYWxseSBtaXRvY2hvbmRyaWFsIGNvbnRlbnQgaXMgdW5pZm9ybSBhbmQgbG93IGFjcm9zcyBhbGwgdG9vbHMgYW5kIGFsbCBzYW1wbGVzLCB3aGljaCBpcyBncmVhdC4gCgpCZWZvcmUgZG9pbmcgYW55bW9yZSBwbG90dGluZywgbGV0J3Mgc3BsaXQgb3VyIGRhdGFmcmFtZSBieSBzaW5nbGUtY2VsbCBhbmQgc2luZ2xlLW51Y2xldXMgUk5BLXNlcSBzYW1wbGVzLiAKCmBgYHtyfQpjZWxsX2NvbGRhdGFfcWMgPC0gY29sZGF0YV9pbmZvX2RmICU+JQogIGRwbHlyOjpmaWx0ZXIoc2VxX3VuaXQgPT0gImNlbGwiKQoKbnVjbGV1c19jb2xkYXRhX3FjIDwtIGNvbGRhdGFfaW5mb19kZiAlPiUKICBkcGx5cjo6ZmlsdGVyKHNlcV91bml0ID09ICJudWNsZXVzIikgJT4lCiAgZHBseXI6OmZpbHRlcihpbmRleF90eXBlID09ICJzcGxpY2kiKQpgYGAKCiMjIyBQZXIgQ2VsbCBRQyBNZXRyaWNzIGluIGFsbCBjZWxscwoKV2UgYXJlIGdvaW5nIHRvIGxvb2sgYXQgc29tZSBRQyBtZXRyaWNzIGF0IGEgcGVyIGNlbGwgbGV2ZWwuIApTcGVjaWZpY2FsbHkgd2Ugd2lsbCBsb29rIGF0IFVNSS9jZWxsIGFuZCBnZW5lcyBkZXRlY3RlZC9jZWxsLiAKCmBgYHtyIGZpZy5oZWlnaHQ9MiwgZmlnLndpZHRoPTV9CmdncGxvdChudWNsZXVzX2NvbGRhdGFfcWMsIGFlcyh4ID0gYWxldmluX3Jlc29sdXRpb24sIHkgPSBzdW0sIGZpbGwgPSB0b29sKSkgKyAKICBnZW9tX2JveHBsb3QoKSArIAogIGZhY2V0X2dyaWQofiBzYW1wbGUpICsKICB0aGVtZV9jbGFzc2ljKCkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKSArIAogIHlsYWIoIlVNSS9jZWxsIikgKyAKICB4bGFiKCIiKSArCiAgY29vcmRfY2FydGVzaWFuKHlsaW0gPSBjKDAsMzAwMDApKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoY2VsbF9jb2xkYXRhX3FjLCBhZXMoeCA9IGFsZXZpbl9yZXNvbHV0aW9uLCB5ID0gc3VtLCBmaWxsID0gdG9vbCkpICsgCiAgZ2VvbV9ib3hwbG90KCkgKyAKICBmYWNldF9ncmlkKHNhbXBsZSB+IGluZGV4X3R5cGUpICsKICB0aGVtZV9jbGFzc2ljKCkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKSArIAogIHlsYWIoIlVNSS9jZWxsIikgKyAKICB4bGFiKCIiKSArIAogIHlsaW0oYygwLDUwMDAwKSkKYGBgCkluIGxvb2tpbmcgYXQgVU1JL2NlbGwgYmV0d2VlbiBjZWxsIGFuZCBudWNsZXVzIHNhbXBsZXMsIGl0J3MgdmVyeSBjbGVhciB0byBtZSB0aGF0IHRoZSBzaW5nbGUtbnVjbGVpIHNhbXBsZXMgaGF2ZSBhIGJpdCBtb3JlIGZsdWN0dWF0aW9uIC0gYm90aCBhY3Jvc3Mgc2FtcGxlcyBhbmQgYWNyb3NzIHRvb2xzLiAKVGhlIHNpbmdsZS1jZWxsIHNhbXBsZXMgdGVuZCB0byBiZSBmYWlybHkgdW5pZm9ybSBhY3Jvc3MgYWxsIHRvb2xzLCBhbHRob3VnaCBpdCBsb29rcyBsaWtlIHRoZSBtZWRpYW4gaW4gMTI2IGFuZCAxMjcgaXMgbG93ZXIgaW4gYWxldmluLWZyeSB0aGFuIGluIGNlbGxyYW5nZXIuIApUaGlzIGlzIG5vdCB0aGUgY2FzZSBpbiB0aGUgc2luZ2xlLW51Y2xlaSBzYW1wbGVzIHdoZXJlIHdlIHNlZSB0aGF0IGFsZXZpbi1mcnkgZG9lc24ndCBzZWVtIHRvIGJlIGNhcHR1cmluZyBhcyBtYW55IGNvdW50cywgc3BlY2lmaWNhbGx5IGluIHNhbXBsZSAyMjAuIAoKYGBge3IgZmlnLmhlaWdodD0yLCBmaWcud2lkdGg9NX0KZ2dwbG90KG51Y2xldXNfY29sZGF0YV9xYywgYWVzKHggPSBhbGV2aW5fcmVzb2x1dGlvbiwgeSA9IGRldGVjdGVkLCBmaWxsID0gdG9vbCkpICsgCiAgZ2VvbV9ib3hwbG90KCkgKyAKICBmYWNldF9ncmlkKH4gc2FtcGxlKSArCiAgdGhlbWVfY2xhc3NpYygpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkgKyAKICB5bGFiKCJHZW5lcyBkZXRlY3RlZC9jZWxsIikgKyAKICB4bGFiKCIiKSArCiAgY29vcmRfY2FydGVzaWFuKHlsaW0gPSBjKDAsMTUwMDApKQpgYGAKSGVyZSwgd2Ugc2VlIGV2ZW4gbW9yZSB2YXJpYXRpb24gaW4gZ2VuZXMgZGV0ZWN0ZWQgcGVyIGNlbGwgaW4gdGhlIHNpbmdsZS1udWNsZWkgc2FtcGxlcyB3aXRoIGNlbGxyYW5nZXIgdGVuZGluZyB0byBoYXZlIG11Y2ggdGlnaHRlciBJUVIncyB0aGFuIGFsZXZpbi1mcnkuIApIb3dldmVyLCB3ZSBhcmUgc3RpbGwgbG9va2luZyBhdCBhbGwgY2VsbHMgYW5kIG5vdCBqdXN0IHNoYXJlZCBjZWxscyBvbmx5LCBzbyB0aGF0IGNvdWxkIGJlIHBhcnQgb2YgdGhlIGlzc3VlLiAKQWxldmluLWZyeSBjb3VsZCBiZSBkZXRlY3RpbmcgbW9yZSBjZWxscyB3aXRoIGxvd2VyIGNvdW50cyB0aGFuIGNlbGxyYW5nZXIuIApgYGB7cn0KZ2dwbG90KGNlbGxfY29sZGF0YV9xYywgYWVzKHggPSBhbGV2aW5fcmVzb2x1dGlvbiwgeSA9IGRldGVjdGVkLCBmaWxsID0gdG9vbCkpICsgCiAgZ2VvbV9ib3hwbG90KCkgKyAKICBmYWNldF9ncmlkKHNhbXBsZSB+IGluZGV4X3R5cGUpICsKICB0aGVtZV9jbGFzc2ljKCkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKSArIAogIHlsYWIoIkdlbmVzIGRldGVjdGVkL2NlbGwiKSArIAogIHhsYWIoIiIpICsgCiAgeWxpbShjKDAsMTAwMDApKQpgYGAKCgojIyMgUGVyIENlbGwgUUMgTWV0cmljcyBpbiBTaGFyZWQgQ2VsbHMgb25seSwgTm8gTWluaW11bSBHZW5lIENvdmVyYWdlCgpOb3cgbGV0J3MgbG9vayBhdCB0aGUgc2FtZSBtZXRyaWNzIGJ1dCBpbiBzaGFyZWQgY2VsbHMgb25seSB0byBzZWUgaWYgYW55IG9mIHRoZXNlIGZsdWN0dWF0aW9ucyBhY3Jvc3MgdG9vbHMgYXJlIGJlY2F1c2UgZGlmZmVyZW50IGNlbGxzIGFyZSBiZWluZyBpZGVudGlmaWVkLiAKCmBgYHtyfQojIGZpbHRlciBmb3IgY2VsbHMgdGhhdCBhcmUgZm91bmQgaW4gYWxsIGNvbmZpZ3VyYXRpb25zIG9mIGFsZXZpbiArIGNlbGxyYW5nZXIKY2VsbF9jb3VudHMgPC0gY2VsbF9jb2xkYXRhX3FjICU+JSAgCiAgZHBseXI6OmNvdW50KGNlbGxfaWQsIHNhbXBsZSkKCmNvbW1vbl9jZWxscyA8LSBjZWxsX2NvdW50cyAlPiUKICBkcGx5cjo6ZmlsdGVyKG4gPT0gOSkgJT4lCiAgZHBseXI6OnB1bGwoY2VsbF9pZCkKCmNlbGxfcWNfY29tbW9uIDwtIGNlbGxfY29sZGF0YV9xYyAlPiUKICBkcGx5cjo6ZmlsdGVyKAogICAgKGNlbGxfaWQgJWluJSBjb21tb25fY2VsbHMpIAogICkKYGBgCgpgYGB7cn0KIyBmaWx0ZXIgZm9yIGNlbGxzIHRoYXQgYXJlIGZvdW5kIGluIGFsbCBjb25maWd1cmF0aW9ucyBvZiBhbGV2aW4gKyBjZWxscmFuZ2VyCm51Y2xlaV9jb3VudHMgPC0gbnVjbGV1c19jb2xkYXRhX3FjICU+JQogIGRwbHlyOjpjb3VudChjZWxsX2lkLCBzYW1wbGUpCgpjb21tb25fbnVjbGVpIDwtIG51Y2xlaV9jb3VudHMgJT4lCiAgZHBseXI6OmZpbHRlcihuID09IDUpICU+JQogIGRwbHlyOjpwdWxsKGNlbGxfaWQpCgpudWNsZXVzX3FjX2NvbW1vbiA8LSBudWNsZXVzX2NvbGRhdGFfcWMgJT4lCiAgZHBseXI6OmZpbHRlcigKICAgIChjZWxsX2lkICVpbiUgY29tbW9uX251Y2xlaSkKICApCmBgYAoKRG9lcyBtaXRvY2hvbmRyaWFsIGNvbnRlbnQgY2hhbmdlIHdoZW4gd2Ugb25seSBsb29rIGF0IHNoYXJlZCBkYXRhPwpJIGV4cGVjdCBub3Qgc2luY2UgaXQgd2FzIGFscmVhZHkgcHJldHR5IHVuaWZvcm0uIAoKYGBge3IgZmlnLmhlaWdodCA9IDUsIGZpZy53aWR0aCA9MTB9CiMgbWl0byBjb21wYXJpc29uIGFjcm9zcyBzaGFyZWQgY2VsbHMgb25seSBvZiBhbGwgcnVucwojIG51Y2xldXMgc2FtcGxlcyBmaXJzdCAKZ2dwbG90KG51Y2xldXNfcWNfY29tbW9uLCBhZXMoeCA9IGFsZXZpbl9yZXNvbHV0aW9uLCB5ID0gc3Vic2V0c19taXRvX3BlcmNlbnQsIGZpbGwgPSB0b29sKSkgKyAKICBnZW9tX2JveHBsb3QoKSArIAogIGZhY2V0X2dyaWQofiBzYW1wbGUpICsgCiAgdGhlbWVfY2xhc3NpYygpICsgCiAgeWxhYigiJSBNaXRvIC9DZWxsIikgKyAKICB0aGVtZShheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpKQoKIyBzaW5nbGUgY2VsbApnZ3Bsb3QoY2VsbF9xY19jb21tb24sIGFlcyh4ID0gYWxldmluX3Jlc29sdXRpb24sIHkgPSBzdWJzZXRzX21pdG9fcGVyY2VudCwgZmlsbCA9IHRvb2wpKSArIAogIGdlb21fYm94cGxvdCgpICsgCiAgZmFjZXRfZ3JpZChzYW1wbGUgfiBpbmRleF90eXBlKSArIAogIHRoZW1lX2NsYXNzaWMoKSArIAogIHlsYWIoIiUgTWl0byAvQ2VsbCIpICsgCiAgdGhlbWUoYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpLCBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgClRoZSBhbnN3ZXIgaXMgbm8sIGl0IHNlZW1zIHRvIHN0aWxsIGJlIHF1aXRlIHNpbWlsYXIsIGFsdGhvdWdoIGhlcmUgSSBhbSBwbG90dGluZyBpdCBieSBicmVha2luZyBvdXQgdGhlIHNpbmdsZS1udWNsZWkgc2FtcGxlcyBhbmQgeW91IGNhbiBzZWUgdGhhdCBTQ1BDUjAwMDExOCBoYXMgaGlnaGVyIG1pdG8gY29udGVudCB0aGFuIHRoZSBvdGhlciBzYW1wbGVzIGFuZCBmbHVjdHVhdGVzIGFjcm9zcyB0b29scy4gCkluIHRoZSBwcmV2aW91cyBiZW5jaG1hcmtpbmcgSSB3YXMgd29ycmllZCBhYm91dCB0aGlzIHNhbXBsZSBub3QgYmVpbmcgaGlnaCBxdWFsaXR5IGFuZCBhbHRob3VnaCB0ZWNobmljYWxseSB0aGUgbWl0byBjb250ZW50IGlzIHN0aWxsIGJlbG93IDIwJSwgaXQgZG9lc24ndCBsb29rIGFzIHVuaWZvcm0gYXMgaW4gMjIwIG9yIDIyMS4gCgpMZXQncyBsb29rIGF0IFVNSS9jZWxsIGFuZCBnZW5lcyBkZXRlY3RlZC9jZWxsIGluIHRoZSBzaGFyZWQgY2VsbHMuIAoKYGBge3IgZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9M30KZ2dwbG90KG51Y2xldXNfcWNfY29tbW9uLCBhZXMoeCA9IGFsZXZpbl9yZXNvbHV0aW9uLCB5ID0gc3VtLCBmaWxsID0gdG9vbCkpICsgCiAgZ2VvbV9ib3hwbG90KCkgKyAKICBmYWNldF9ncmlkKH4gc2FtcGxlKSArCiAgdGhlbWVfY2xhc3NpYygpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkgKyAKICB5bGFiKCJVTUkvY2VsbCIpICsgCiAgeGxhYigiIikgKwogIGNvb3JkX2NhcnRlc2lhbih5bGltID0gYygwLDMwMDAwKSkKYGBgCmBgYHtyfQpnZ3Bsb3QoY2VsbF9xY19jb21tb24sIGFlcyh4ID0gYWxldmluX3Jlc29sdXRpb24sIHkgPSBzdW0sIGZpbGwgPSB0b29sKSkgKyAKICBnZW9tX2JveHBsb3QoKSArIAogIGZhY2V0X2dyaWQoc2FtcGxlIH4gaW5kZXhfdHlwZSkgKwogIHRoZW1lX2NsYXNzaWMoKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpICsgCiAgeWxhYigiVU1JL2NlbGwiKSArIAogIHhsYWIoIiIpICsgCiAgeWxpbShjKDAsNTAwMDApKQpgYGAKYGBge3IgZmlnLndpZHRoID0gNSwgZmlnLmhlaWdodD0zfQpnZ3Bsb3QobnVjbGV1c19xY19jb21tb24sIGFlcyh4ID0gYWxldmluX3Jlc29sdXRpb24sIHkgPSBkZXRlY3RlZCwgZmlsbCA9IHRvb2wpKSArIAogIGdlb21fYm94cGxvdCgpICsgCiAgZmFjZXRfZ3JpZCh+IHNhbXBsZSkgKwogIHRoZW1lX2NsYXNzaWMoKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpICsgCiAgeWxhYigiR2VuZXMgZGV0ZWN0ZWQvY2VsbCIpICsgCiAgeGxhYigiIikgKwogIGNvb3JkX2NhcnRlc2lhbih5bGltID0gYygwLDE1MDAwKSkKYGBgCmBgYHtyfQpnZ3Bsb3QoY2VsbF9xY19jb21tb24sIGFlcyh4ID0gYWxldmluX3Jlc29sdXRpb24sIHkgPSBkZXRlY3RlZCwgZmlsbCA9IHRvb2wpKSArIAogIGdlb21fYm94cGxvdCgpICsgCiAgZmFjZXRfZ3JpZCh+IHNhbXBsZSkgKwogIHRoZW1lX2NsYXNzaWMoKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpICsgCiAgeWxhYigiR2VuZXMgZGV0ZWN0ZWQvY2VsbCIpICsgCiAgeGxhYigiIikgKwogIGNvb3JkX2NhcnRlc2lhbih5bGltID0gYygwLDEwMDAwKSkKYGBgCk92ZXJhbGwsIGl0IGxvb2tzIGxpa2Ugd2UgaGF2ZSBlbGltaW5hdGVkIG1hbnkgb2YgdGhlIGRpZmZlcmVuY2VzIGJldHdlZW4gdGhlIHRvb2xzIGluIHRoZSBzaW5nbGUtbnVjbGVpIFJOQS1zZXEgZGF0YS4gCkFsdGhvdWdoIGl0IGFwcGVhcnMgdGhhdCBhY3Jvc3MgdGhlIGJvYXJkIHRoZSBjci1saWtlIHJlc29sdXRpb24gZ2l2ZXMgbW9yZSBvbiBwYXIgZGlzdHJpYnV0aW9ucyB0byBjZWxscmFuZ2VyIHRoYW4gdGhlIGZ1bGwgcmVzb2x1dGlvbi4gClRoZSBmdWxsIHJlc29sdXRpb24gc2VlbXMgdG8gaGF2ZSBoaWdoZXIgVU1Jcy9jZWxsIGFuZCBnZW5lcy9jZWxsIGFjcm9zcyBtb3N0IHNhbXBsZXMuIApJJ20gbm90IGVudGlyZWx5IHN1cmUgaG93IHRvIGludGVycHJldCB0aGF0IGF0IHRoaXMgcG9pbnQuIAoKSXQgZG9lcyBsb29rIGxpa2UgYWxldmluLWZyeSBpcyBkZXRlY3RpbmcgbW9yZSBjZWxscyB0aGFuIGNlbGxyYW5nZXIsIHNvIHBlcmhhcHMgdGhlcmUgaXMgc29tZXRoaW5nIHdlIGNvdWxkIGJlIGRvaW5nIGF0IHRoZSBmaWx0ZXJpbmcgc3RhZ2UgdGhhdCBjb3VsZCBoZWxwIGltcHJvdmUgdGhpcz8gCgpMZXQncyB0YWtlIGEgcXVpY2sgbG9vayBhdCB0aGUgbnVtYmVyIG9mIGNlbGxzIGRldGVjdGVkIGJ5IHRvb2wganVzdCB0byBiZSBzdXJlIHRoYXQgdGhpcyBoeXBvdGhlc2lzIGlzIGNvcnJlY3QuIApgYGB7ciBmaWcud2lkdGg9N30KY2VsbF9udW1iZXJzIDwtIGNvbGRhdGFfaW5mb19kZiAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KHF1YW50X2lkLCB0b29sLCBzYW1wbGUpICU+JSAKICAjZHBseXI6OmZpbHRlcihzYW1wbGUgIT0gIlNDUENSMDAwMTE4IikgJT4lCiAgZHBseXI6OnRhbGx5KCkKCmdncGxvdChjZWxsX251bWJlcnMsIGFlcyh4ID0gc2FtcGxlLCB5ID0gbiwgY29sb3IgPSB0b29sKSkgKyAKICBnZW9tX3BvaW50KCkgKwogIHRoZW1lX2NsYXNzaWMoKQpgYGAKQXMgZXhwZWN0ZWQsIGZvciBhbGwgc2FtcGxlcyAoZXhjZXB0IFNDUENSMDAwMDA2KSwgY2VsbHJhbmdlciBpcyBkZXRlY3RpbmcgbGVzcyBjZWxscyB0aGFuIGluIGFsZXZpbi1mcnkuIApJJ20gYWxzbyBub3Qgc3VyZSB3aGF0IGhhcyBoYXBwZW5lZCB3aXRoIFNDUENSMDAwMTE4LCBidXQgd2Ugc2F3IHRoaXMgcHJldmlvdXNseSBhbmQgaXQgYWxzbyBzaG93cyBzdHJhbmdlIHBhdHRlcm5zIGluIG1pdG9jaG9uZHJpYWwgY29udGVudCBhbmQgdmVyeSBsb3cgY292ZXJhZ2UgYWNyb3NzIHRoZSBib2FyZC4KCgojIyBQZXIgQ2VsbCBRQyBNZXRyaWNzIGluIFNoYXJlZCBDZWxscywgTWluaW11bSBHZW5lIENvdmVyYWdlIAoKVG8gc2VlIGlmIHRoZSBpbmNyZWFzZSBpbiBnZW5lcyBkZXRlY3RlZC9jZWxsIGluIHRoZSBmdWxsIHJlc29sdXRpb24gY291bGQgYmUgZHVlIHRvIGxvdyBjb3ZlcmVkIGdlbmVzLCBsZXQncyBsb29rIGF0IHRoZSBudW1iZXIgb2YgZ2VuZXMgZGV0ZWN0ZWQgaWYgd2Ugd2VyZSB0byByZW1vdmUgZ2VuZXMgb25seSBmb3VuZCBpbiA8IDUlIG9mIGNlbGxzLiAKCgpgYGB7cn0KIyB1c2UgYWRkQ2VsbFFDIGZ1bmN0aW9uIHRvIGFkZFBlckNlbGxRQyB3aXRoIGdlbmUgZGV0ZWN0aW9uIHRocmVzaG9sZCBmb3IgYWxsIHNjZXMgaW4gdGhlIHNjZV9saXN0CmZvcihsaXN0IGluIHNjZV9saXN0KXsKICBsaXN0IDwtIGxpc3QgJT4lCiAgICBwdXJycjo6bWFwKGFkZENlbGxRQywgbWl0byA9IG1pdG9fZ2VuZXMsIHRocmVzaG9sZCA9IDUpCn0KYGBgCgoKYGBge3J9CiMgbWVyZ2UgYmFjayBpbnRvIGEgZGF0YWZyYW1lIGZvciBwbG90dGluZyAKZGZfbGlzdCA8LSBsaXN0KCkKaSA9IDEKZm9yKGxpc3QgaW4gc2NlX2xpc3QpewogIGRmX2xpc3RbW2ldXSA8LSBsaXN0ICU+JQogICAgcHVycnI6Om1hcF9kZihjb2xkYXRhX3RvX2RmLCAuaWQgPSAicXVhbnRfaWQiKQogIGkgPSBpICsgMQp9CgojIGNoYW5nZSBuYW1lcyBvZiB0aGUgc2NlIGxpc3QgdG8gYmUgdGhlIHRvb2wgdXNlZCBmaXJzdApuYW1lcyhkZl9saXN0KSA8LSBjKHJlcCgiYWxldmluLWZyeSIsIDgpLCAiY2VsbHJhbmdlciIpCmNvbGRhdGFfdGhyZXNob2xkIDwtIGRmX2xpc3QgJT4lCiAgZHBseXI6OmJpbmRfcm93cyguaWQgPSAidG9vbCIpCgpgYGAKCmBgYHtyfQojIG1lcmdlIG5ldyBjb2xkYXRhIGJhY2sgd2l0aCBxdWFudF9pbmZvCmNvbGRhdGFfaW5mb190aHJlc2hvbGRfZGYgPC0gY29sZGF0YV90aHJlc2hvbGQgJT4lCiAgZHBseXI6OmxlZnRfam9pbihxdWFudF9pbmZvLAogICAgICAgICAgICAgICAgICAgYnkgPSBjKCJ0b29sIiA9ICJ0b29sIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgInF1YW50X2lkIiA9ICJxdWFudF9kaXIiKSkKCiMgYnJlYWsgb3V0IGludG8gc2luZ2xlIGNlbGwgYW5kIHNpbmdsZSBudWNsZXVzIApjZWxsX2NvbGRhdGFfdGhyZXNob2xkX3FjIDwtIGNvbGRhdGFfaW5mb190aHJlc2hvbGRfZGYgJT4lCiAgZHBseXI6OmZpbHRlcihzZXFfdW5pdCA9PSAiY2VsbCIpCgpudWNsZXVzX2NvbGRhdGFfdGhyZXNob2xkX3FjIDwtIGNvbGRhdGFfaW5mb190aHJlc2hvbGRfZGYgJT4lCiAgZHBseXI6OmZpbHRlcihzZXFfdW5pdCA9PSAibnVjbGV1cyIgJiBpbmRleF90eXBlID09ICJzcGxpY2kiKQpgYGAKCmBgYHtyfQojIGxvb2sgZm9yIHNoYXJlZCBjZWxscyBvbmx5IApjZWxsX2NvdW50c190aHJlc2hvbGQgPC0gY2VsbF9jb2xkYXRhX3RocmVzaG9sZF9xYyAlPiUgIAogIGRwbHlyOjpjb3VudChjZWxsX2lkLCBzYW1wbGUpCgpjb21tb25fY2VsbHNfdGhyZXNob2xkIDwtIGNlbGxfY291bnRzX3RocmVzaG9sZCAlPiUKICBkcGx5cjo6ZmlsdGVyKG4gPT0gOSkgJT4lCiAgZHBseXI6OnB1bGwoY2VsbF9pZCkKCmNlbGxfcWNfY29tbW9uX3RocmVzaG9sZCA8LSBjZWxsX2NvbGRhdGFfdGhyZXNob2xkX3FjICU+JQogIGRwbHlyOjpmaWx0ZXIoCiAgICAoY2VsbF9pZCAlaW4lIGNvbW1vbl9jZWxsc190aHJlc2hvbGQpIAogICkKYGBgCgpgYGB7cn0KIyBsb29rIGZvciBzaGFyZWQgY2VsbHMgb25seSAKbnVjbGVpX2NvdW50c190aHJlc2hvbGQgPC0gbnVjbGV1c19jb2xkYXRhX3RocmVzaG9sZF9xYyAlPiUKICBkcGx5cjo6Y291bnQoY2VsbF9pZCwgc2FtcGxlKQoKY29tbW9uX251Y2xlaV90aHJlc2hvbGQgPC0gbnVjbGVpX2NvdW50c190aHJlc2hvbGQgJT4lCiAgZHBseXI6OmZpbHRlcihuID09IDUpICU+JQogIGRwbHlyOjpwdWxsKGNlbGxfaWQpCgpudWNsZXVzX3FjX2NvbW1vbl90aHJlc2hvbGQgPC0gbnVjbGV1c19jb2xkYXRhX3RocmVzaG9sZF9xYyAlPiUKICBkcGx5cjo6ZmlsdGVyKAogICAgKGNlbGxfaWQgJWluJSBjb21tb25fbnVjbGVpX3RocmVzaG9sZCkKICApCmBgYAoKCmBgYHtyIGZpZy53aWR0aD01LCBmaWcuaGVpZ2h0PTN9CmdncGxvdChudWNsZXVzX3FjX2NvbW1vbl90aHJlc2hvbGQsIGFlcyh4ID0gYWxldmluX3Jlc29sdXRpb24sIHkgPSBkZXRlY3RlZCwgZmlsbCA9IHRvb2wpKSArIAogIGdlb21fYm94cGxvdCgpICsgCiAgZmFjZXRfZ3JpZCh+IHNhbXBsZSkgKwogIHRoZW1lX2NsYXNzaWMoKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpICsgCiAgeWxhYigiR2VuZXMgZGV0ZWN0ZWQvY2VsbCIpICsgCiAgeGxhYigiIikgKwogIGNvb3JkX2NhcnRlc2lhbih5bGltID0gYygwLDE1MDAwKSkKYGBgCmBgYHtyIGZpZy53aWR0aD01LCBmaWcuaGVpZ2h0PTN9CmdncGxvdChjZWxsX3FjX2NvbW1vbl90aHJlc2hvbGQsIGFlcyh4ID0gYWxldmluX3Jlc29sdXRpb24sIHkgPSBkZXRlY3RlZCwgZmlsbCA9IHRvb2wpKSArIAogIGdlb21fYm94cGxvdCgpICsgCiAgZmFjZXRfZ3JpZChzYW1wbGUgfiBpbmRleF90eXBlKSArCiAgdGhlbWVfY2xhc3NpYygpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkgKyAKICB5bGFiKCJHZW5lcyBkZXRlY3RlZC9jZWxsIikgKyAKICB4bGFiKCIiKSArCiAgY29vcmRfY2FydGVzaWFuKHlsaW0gPSBjKDAsMTAwMDApKQpgYGAKSXQgZG9lcyBub3QgYXBwZWFyIHRoYXQgdGhlIGluY3JlYXNlIGluIGdlbmVzIGRldGVjdGVkIG9ic2VydmVkIGluIHRoZSBmdWxsIHJlc29sdXRpb24gaXMgcmVzb2x2ZWQgYnkgcmVtb3ZpbmcgbG93bHkgY292ZXJlZCBnZW5lcy4gCgojIyMgUGVyIENlbGwgQ29ycmVsYXRpb25zIGFjcm9zcyB0b29scwoKVGhlIGxhc3QgY29tcGFyaXNvbiB3ZSB3aWxsIG1ha2UgYXQgdGhlIHBlciBjZWxsIGxldmVsIGlzIHRvIGxvb2sgYXQgdGhlIGNvcnJlbGF0aW9uIG9mIFVNSS9jZWxsIGFjcm9zcyB0b29scy4gCkhlcmUgd2Ugd2lsbCBjb21wYXJlIGVhY2ggb2YgdGhlIGFsZXZpbi1mcnkgY29uZmlndXJhdGlvbnMgdG8gY2VsbHJhbmdlciBhbmQgbG9vayBhdCB0aGUgY29ycmVsYXRpb24gY29lZmZpY2llbnQgYW5kIHBsb3Qgc29tZSBvZiB0aGUgaW5kaXZpZHVhbCB2YWx1ZXMgb2YgVU1JL2NlbGwgdG8gc2VlIGhvdyB3ZWxsIGNvcnJlbGF0ZWQgdGhlc2UgdG9vbHMgYXJlLiAKCgpgYGB7cn0KIyBzcHJlYWQgdGFibGUgZm9yIGNvbXBhcmlzb25zCmNlbGxfcWNfY29tbW9uX2NvciA8LSBjZWxsX3FjX2NvbW1vbiAlPiUKICBkcGx5cjo6c2VsZWN0KHRvb2wsIGluZGV4X3R5cGUsIGFsZXZpbl9yZXNvbHV0aW9uLCBhbGV2aW5fYWxpZ25tZW50LCBjZWxsX2lkLCBzYW1wbGUsIHN1bSkgJT4lCiAgIyBzcHJlYWQgdGhlIG1lYW4gZXhwcmVzc2lvbiBzdGF0cyB0byBvbmUgY29sdW1uIHBlciBjYWxsZXIKICB0aWR5cjo6cGl2b3Rfd2lkZXIoaWRfY29scyA9IGMoY2VsbF9pZCwgc2FtcGxlKSwKICAgICAgICAgICAgICAgICAgICAgbmFtZXNfZnJvbSA9IGMoInRvb2wiLCAiaW5kZXhfdHlwZSIsICJhbGV2aW5fcmVzb2x1dGlvbiIsICJhbGV2aW5fYWxpZ25tZW50IiksCiAgICAgICAgICAgICAgICAgICAgIHZhbHVlc19mcm9tID0gc3VtKSAlPiUKICAjIGRyb3Agcm93cyB3aXRoIE5BIHZhbHVlcyB0byBlYXNlIGNvcnJlbGF0aW9uIGNhbGN1bGF0aW9ucwogIHRpZHlyOjpkcm9wX25hKCkKCm51Y2xldXNfcWNfY29tbW9uX2NvciA8LSBudWNsZXVzX3FjX2NvbW1vbiAlPiUKICBkcGx5cjo6c2VsZWN0KHRvb2wsIGluZGV4X3R5cGUsIGFsZXZpbl9yZXNvbHV0aW9uLCBhbGV2aW5fYWxpZ25tZW50LCBjZWxsX2lkLCBzYW1wbGUsIHN1bSkgJT4lCiAgdGlkeXI6OnBpdm90X3dpZGVyKGlkX2NvbHMgPSBjKGNlbGxfaWQsIHNhbXBsZSksCiAgICAgICAgICAgICAgICAgICAgIG5hbWVzX2Zyb20gPSBjKCJ0b29sIiwgImluZGV4X3R5cGUiLCAiYWxldmluX3Jlc29sdXRpb24iLCAiYWxldmluX2FsaWdubWVudCIpLAogICAgICAgICAgICAgICAgICAgICB2YWx1ZXNfZnJvbSA9IHN1bSkgJT4lCiAgdGlkeXI6OmRyb3BfbmEoKQpgYGAKCgpgYGB7cn0KY2VsbF9xY19jb21tb25fY29yICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoc2FtcGxlKSAlPiUKICBkcGx5cjo6c3VtbWFyaXplKAogICAgY0ROQV9zYWxpZ25fZnVsbF9jb3IgPSBjb3IoYGNlbGxyYW5nZXJfY0ROQV9ub3RfYWxldmluX25vdF9hbGV2aW5gLCBgYWxldmluLWZyeV9jRE5BX2Z1bGxfc2FsaWduYCwgbWV0aG9kID0gInNwZWFybWFuIiksCiAgICBjRE5BX3NrZXRjaF9mdWxsX2NvciA9Y29yKGBjZWxscmFuZ2VyX2NETkFfbm90X2FsZXZpbl9ub3RfYWxldmluYCwgYGFsZXZpbi1mcnlfY0ROQV9mdWxsX3NrZXRjaGAsIG1ldGhvZCA9ICJzcGVhcm1hbiIpLAogICAgY0ROQV9zYWxpZ25fY3JfY29yID0gY29yKGBjZWxscmFuZ2VyX2NETkFfbm90X2FsZXZpbl9ub3RfYWxldmluYCwgYGFsZXZpbi1mcnlfY0ROQV9jcl9zYWxpZ25gLCBtZXRob2QgPSAic3BlYXJtYW4iKSwKICAgIGNETkFfc2tldGNoX2NyX2NvciA9IGNvcihgY2VsbHJhbmdlcl9jRE5BX25vdF9hbGV2aW5fbm90X2FsZXZpbmAsIGBhbGV2aW4tZnJ5X2NETkFfY3Jfc2tldGNoYCwgbWV0aG9kID0gInNwZWFybWFuIiksIAogICAgc3BsaWNpX3NhbGlnbl9mdWxsX2NvciA9IGNvcihgY2VsbHJhbmdlcl9jRE5BX25vdF9hbGV2aW5fbm90X2FsZXZpbmAsIGBhbGV2aW4tZnJ5X3NwbGljaV9mdWxsX3NhbGlnbmAsIG1ldGhvZCA9ICJzcGVhcm1hbiIpLAogICAgc3BsaWNpX3NrZXRjaF9mdWxsX2NvciA9IGNvcihgY2VsbHJhbmdlcl9jRE5BX25vdF9hbGV2aW5fbm90X2FsZXZpbmAsIGBhbGV2aW4tZnJ5X3NwbGljaV9mdWxsX3NrZXRjaGAsIG1ldGhvZCA9ICJzcGVhcm1hbiIpLAogICAgc3BsaWNpX3NhbGlnbl9jcl9jb3IgPSBjb3IoYGNlbGxyYW5nZXJfY0ROQV9ub3RfYWxldmluX25vdF9hbGV2aW5gLCBgYWxldmluLWZyeV9zcGxpY2lfY3Jfc2FsaWduYCwgbWV0aG9kID0gInNwZWFybWFuIiksCiAgICBzcGxpY2lfc2tldGNoX2NyX2NvciA9IGNvcihgY2VsbHJhbmdlcl9jRE5BX25vdF9hbGV2aW5fbm90X2FsZXZpbmAsIGBhbGV2aW4tZnJ5X3NwbGljaV9jcl9za2V0Y2hgLCBtZXRob2QgPSAic3BlYXJtYW4iKQogICkKYGBgCgpgYGB7cn0KbnVjbGV1c19xY19jb21tb25fY29yICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoc2FtcGxlKSAlPiUKICBkcGx5cjo6c3VtbWFyaXplKAogICAgc3BsaWNpX3NhbGlnbl9mdWxsX2NvciA9IGNvcihgY2VsbHJhbmdlcl9zcGxpY2lfbm90X2FsZXZpbl9ub3RfYWxldmluYCwgYGFsZXZpbi1mcnlfc3BsaWNpX2Z1bGxfc2FsaWduYCwgbWV0aG9kID0gInNwZWFybWFuIiksCiAgICBzcGxpY2lfc2tldGNoX2Z1bGxfY29yID0gY29yKGBjZWxscmFuZ2VyX3NwbGljaV9ub3RfYWxldmluX25vdF9hbGV2aW5gLCBgYWxldmluLWZyeV9zcGxpY2lfZnVsbF9za2V0Y2hgLCBtZXRob2QgPSAic3BlYXJtYW4iKSwKICAgIHNwbGljaV9zYWxpZ25fY3JfY29yID0gY29yKGBjZWxscmFuZ2VyX3NwbGljaV9ub3RfYWxldmluX25vdF9hbGV2aW5gLCBgYWxldmluLWZyeV9zcGxpY2lfY3Jfc2FsaWduYCwgbWV0aG9kID0gInNwZWFybWFuIiksCiAgICBzcGxpY2lfc2tldGNoX2NyX2NvciA9IGNvcihgY2VsbHJhbmdlcl9zcGxpY2lfbm90X2FsZXZpbl9ub3RfYWxldmluYCwgYGFsZXZpbi1mcnlfc3BsaWNpX2NyX3NrZXRjaGAsIG1ldGhvZCA9ICJzcGVhcm1hbiIpCiAgKQpgYGAKV2UgaGF2ZSBwcmV2aW91c2x5IGxvb2tlZCBhdCBzYW1wbGUgMTE4IGFuZCAxMTkgYW5kIHRoZXkgaGF2ZSB2ZXJ5IGZldyBjZWxscyB0aGF0IGFyZSBpZGVudGlmaWVkLCB3aGljaCBpcyB3aHkgd2UgYWRkZWQgaW4gc2FtcGxlIDIyMCBhbmQgMjIxLiAKSGVyZSwgSSBhbSBsb29raW5nIGF0IDIyMCBhbmQgMjIxIHdoaWNoIGJvdGggaGF2ZSA+IDUwMDAgY2VsbHMgbWFraW5nIHRoZW0gYmV0dGVyIGNhbmRpZGF0ZXMgZm9yIHRoZXNlIGNvbXBhcmlzb25zLiAKQWxsIG9mIHRoZXNlIHRvb2xzIGhhdmUgdmVyeSBoaWdoIGNvcnJlbGF0aW9ucyB3aXRoIGNlbGxyYW5nZXIgZm9yIGJvdGggdGhlIHNpbmdsZS1jZWxsIGFuZCBzaW5nbGUtbnVjbGV1cyBzYW1wbGVzLiAKQmVsb3csIEkgaGF2ZSBtYWRlIGEgZmV3IHBsb3RzIHNob3dpbmcgdGhlIGRpcmVjdCBjb21wYXJpc29ucyBmb3IgYWxsIG9mIHRoZSB2YXJpYXRpb25zIHVzaW5nIHRoZSBgLS1za2V0Y2hgIG9wdGlvbiBpbiBjb21wYXJpc29uIHRvIGNlbGxyYW5nZXIgYW5kIHdlIHNlZSBoaWdoIGNvcnJlbGF0aW9ucyBhY3Jvc3MgdGhlIGJvYXJkLiAKCmBgYHtyfQpnZ3Bsb3QoY2VsbF9xY19jb21tb25fY29yLCBhZXMoeCA9IGBjZWxscmFuZ2VyX2NETkFfbm90X2FsZXZpbl9ub3RfYWxldmluYCwgeSA9IGBhbGV2aW4tZnJ5X2NETkFfZnVsbF9za2V0Y2hgKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDAuNSwgYWxwaGEgPSAwLjEpICsgCiAgZmFjZXRfd3JhcCh+IHNhbXBsZSkgKyAKICBzY2FsZV94X2xvZzEwKCkgKyAKICBzY2FsZV95X2xvZzEwKCkgKyAKICBsYWJzKHggPSAiQ2VsbCBSYW5nZXIgVU1JL2NlbGwiLCB5ID0gIkFsZXZpbiBGcnksIGNETkEgaW5kZXgsIEZ1bGwgUmVzb2x1dGlvbiBTa2V0Y2ggVU1JL2NlbGwiKSArIAogIHRoZW1lX2NsYXNzaWMoKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoY2VsbF9xY19jb21tb25fY29yLCBhZXMoeCA9IGBjZWxscmFuZ2VyX2NETkFfbm90X2FsZXZpbl9ub3RfYWxldmluYCwgeSA9IGBhbGV2aW4tZnJ5X2NETkFfY3Jfc2tldGNoYCkpICsKICBnZW9tX3BvaW50KHNpemUgPSAwLjUsIGFscGhhID0gMC4xKSArIAogIGZhY2V0X3dyYXAofiBzYW1wbGUpICsgCiAgc2NhbGVfeF9sb2cxMCgpICsgCiAgc2NhbGVfeV9sb2cxMCgpICsgCiAgbGFicyh4ID0gIkNlbGwgUmFuZ2VyIFVNSS9jZWxsIiwgeSA9ICJBbGV2aW4gRnJ5LCBjRE5BIGluZGV4LCBjci1saWtlIFJlc29sdXRpb24gU2tldGNoIFVNSS9jZWxsIikgKyAKICB0aGVtZV9jbGFzc2ljKCkKYGBgCmBgYHtyfQpnZ3Bsb3QoY2VsbF9xY19jb21tb25fY29yLCBhZXMoeCA9IGBjZWxscmFuZ2VyX2NETkFfbm90X2FsZXZpbl9ub3RfYWxldmluYCwgeSA9IGBhbGV2aW4tZnJ5X3NwbGljaV9mdWxsX3NrZXRjaGApKSArCiAgZ2VvbV9wb2ludChzaXplID0gMC41LCBhbHBoYSA9IDAuMSkgKyAKICBmYWNldF93cmFwKH4gc2FtcGxlKSArIAogIHNjYWxlX3hfbG9nMTAoKSArIAogIHNjYWxlX3lfbG9nMTAoKSArIAogIGxhYnMoeCA9ICJDZWxsIFJhbmdlciBVTUkvY2VsbCIsIHkgPSAiQWxldmluIEZyeSwgc3BsaWNpIGluZGV4LCBGdWxsIFJlc29sdXRpb24gU2tldGNoIFVNSS9jZWxsIikgKyAKICB0aGVtZV9jbGFzc2ljKCkKYGBgCmBgYHtyfQpnZ3Bsb3QoY2VsbF9xY19jb21tb25fY29yLCBhZXMoeCA9IGBjZWxscmFuZ2VyX2NETkFfbm90X2FsZXZpbl9ub3RfYWxldmluYCwgeSA9IGBhbGV2aW4tZnJ5X3NwbGljaV9jcl9za2V0Y2hgKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDAuNSwgYWxwaGEgPSAwLjEpICsgCiAgZmFjZXRfd3JhcCh+IHNhbXBsZSkgKyAKICBzY2FsZV94X2xvZzEwKCkgKyAKICBzY2FsZV95X2xvZzEwKCkgKyAKICBsYWJzKHggPSAiQ2VsbCBSYW5nZXIgVU1JL2NlbGwiLCB5ID0gIkFsZXZpbiBGcnksIHNwbGljaSBpbmRleCwgY3ItbGlrZSBSZXNvbHV0aW9uIFNrZXRjaCBVTUkvY2VsbCIpICsgCiAgdGhlbWVfY2xhc3NpYygpCmBgYApgYGB7cn0KZ2dwbG90KG51Y2xldXNfcWNfY29tbW9uX2NvciwgYWVzKHggPSBgY2VsbHJhbmdlcl9zcGxpY2lfbm90X2FsZXZpbl9ub3RfYWxldmluYCwgeSA9IGBhbGV2aW4tZnJ5X3NwbGljaV9mdWxsX3NrZXRjaGApKSArCiAgZ2VvbV9wb2ludChzaXplID0gMC41LCBhbHBoYSA9IDAuMSkgKyAKICBmYWNldF93cmFwKH4gc2FtcGxlKSArIAogIHNjYWxlX3hfbG9nMTAoKSArIAogIHNjYWxlX3lfbG9nMTAoKSArIAogIGxhYnMoeCA9ICJDZWxsIFJhbmdlciBVTUkvY2VsbCIsIHkgPSAiQWxldmluIEZyeSwgc3BsaWNpIGluZGV4LCBGdWxsIFJlc29sdXRpb24gU2tldGNoIFVNSS9jZWxsIikgKyAKICB0aGVtZV9jbGFzc2ljKCkKYGBgCgpgYGB7cn0KZ2dwbG90KG51Y2xldXNfcWNfY29tbW9uX2NvciwgYWVzKHggPSBgY2VsbHJhbmdlcl9zcGxpY2lfbm90X2FsZXZpbl9ub3RfYWxldmluYCwgeSA9IGBhbGV2aW4tZnJ5X3NwbGljaV9jcl9za2V0Y2hgKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDAuNSwgYWxwaGEgPSAwLjEpICsgCiAgZmFjZXRfd3JhcCh+IHNhbXBsZSkgKyAKICBzY2FsZV94X2xvZzEwKCkgKyAKICBzY2FsZV95X2xvZzEwKCkgKyAKICBsYWJzKHggPSAiQ2VsbCBSYW5nZXIgVU1JL2NlbGwiLCB5ID0gIkFsZXZpbiBGcnksIHNwbGljaSBpbmRleCwgY3ItbGlrZSBSZXNvbHV0aW9uIFNrZXRjaCBVTUkvY2VsbCIpICsgCiAgdGhlbWVfY2xhc3NpYygpCmBgYAoKIyMgUGVyIEdlbmUgUUMgTWV0cmljcwoKTmV4dCwgd2Ugd2lsbCBsb29rIGF0IHNvbWUgbWV0cmljcyBjb21wYXJpbmcgbWVhbiBnZW5lIGV4cHJlc3Npb24gYWNyb3NzIGdlbmVzIGlkZW50aWZpZWQgZm9yIGVhY2ggc2FtcGxlIHVzaW5nIGVhY2ggdG9vbC4gClRvIGRvIHRoYXQsIHdlIHdpbGwgZmlyc3QgZmlsdGVyIGJ5IG9ubHkgdGhvc2UgZ2VuZXMgdGhhdCBhcmUgZGV0ZWN0ZWQgaW4gbW9yZSB0aGFuIDUlIG9mIGNlbGxzIGFuZCBzaGFyZWQgYWNyb3NzIGFsbCB0b29sIGNvbmZpZ3VyYXRpb25zLiAgCgpgYGB7cn0KIyBjb21iaW5lIHJvd2RhdGEgd2l0aCBxdWFudCBpbmZvCnJvd2RhdGFfaW5mb19kZiA8LSByb3dkYXRhX2RmICU+JQogIGRwbHlyOjptdXRhdGUodG9vbCA9IGRwbHlyOjpjYXNlX3doZW4odG9vbCA9PSAiYWxldmluLWZyeS11bmZpbHRlcmVkIiB+ICJhbGV2aW4tZnJ5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvb2wgPT0gImNlbGxyYW5nZXIiIH4gImNlbGxyYW5nZXIiKSkgJT4lCiAgZHBseXI6OmxlZnRfam9pbihxdWFudF9pbmZvLAogICAgICAgICAgICAgICAgICAgYnkgPSBjKCJ0b29sIiA9ICJ0b29sIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgInF1YW50X2lkIiA9ICJxdWFudF9kaXIiKSkKCgpnZW5lX2NvdW50cyA8LSByb3dkYXRhX2luZm9fZGYgJT4lIAogICMgcmVtb3ZlIGdlbmVzIHRoYXQgaGF2ZSBhIGxvdyBmcmVxdWVuY3kgb2YgYmVpbmcgZGV0ZWN0ZWQKICBkcGx5cjo6ZmlsdGVyKGRldGVjdGVkID49IDUuMCkgJT4lCiAgZHBseXI6OmNvdW50KGdlbmVfaWQsIHNhbXBsZSkKCmNvbW1vbl9nZW5lcyA8LSBnZW5lX2NvdW50cyAlPiUKICBkcGx5cjo6ZmlsdGVyKG4gPT0gOSkgJT4lCiAgZHBseXI6OnB1bGwoZ2VuZV9pZCkKCnJvd2RhdGFfcWNfY29tbW9uIDwtIHJvd2RhdGFfaW5mb19kZiAlPiUKICBkcGx5cjo6ZmlsdGVyKAogICAgKGdlbmVfaWQgJWluJSBjb21tb25fZ2VuZXMpIAogICkKYGBgCgpgYGB7cn0KIyBzcGxpdCBpbnRvIGNlbGwgYW5kIG51Y2xldXMKY2VsbF9yb3dkYXRhX2NvbW1vbiA8LSByb3dkYXRhX3FjX2NvbW1vbiAlPiUKICBkcGx5cjo6ZmlsdGVyKHNlcV91bml0ID09ICJjZWxsIikKbnVjbGV1c19yb3dkYXRhX2NvbW1vbiA8LSByb3dkYXRhX3FjX2NvbW1vbiAlPiUKICBkcGx5cjo6ZmlsdGVyKHNlcV91bml0ID09ICJudWNsZXVzIiAmIGluZGV4X3R5cGUgPT0gInNwbGljaSIpCmBgYAoKCmBgYHtyfQojIHNwcmVhZCB0YWJsZSBmb3IgY29tcGFyaXNvbnMKY2VsbF9yb3dkYXRhX2NvciA8LSBjZWxsX3Jvd2RhdGFfY29tbW9uICU+JQogIGRwbHlyOjpzZWxlY3QodG9vbCwgaW5kZXhfdHlwZSwgYWxldmluX3Jlc29sdXRpb24sIGFsZXZpbl9hbGlnbm1lbnQsIGdlbmVfaWQsIHNhbXBsZSwgbWVhbikgJT4lCiAgIyBzcHJlYWQgdGhlIG1lYW4gZXhwcmVzc2lvbiBzdGF0cyB0byBvbmUgY29sdW1uIHBlciBjYWxsZXIKICB0aWR5cjo6cGl2b3Rfd2lkZXIoaWRfY29scyA9IGMoZ2VuZV9pZCwgc2FtcGxlKSwKICAgICAgICAgICAgICAgICAgICAgbmFtZXNfZnJvbSA9IGMoInRvb2wiLCAiaW5kZXhfdHlwZSIsICJhbGV2aW5fcmVzb2x1dGlvbiIsICJhbGV2aW5fYWxpZ25tZW50IiksCiAgICAgICAgICAgICAgICAgICAgIHZhbHVlc19mcm9tID0gbWVhbikgJT4lCiAgIyBkcm9wIHJvd3Mgd2l0aCBOQSB2YWx1ZXMgdG8gZWFzZSBjb3JyZWxhdGlvbiBjYWxjdWxhdGlvbnMKICB0aWR5cjo6ZHJvcF9uYSgpCgpudWNsZXVzX3Jvd2RhdGFfY29yIDwtIG51Y2xldXNfcm93ZGF0YV9jb21tb24gJT4lCiAgZHBseXI6OnNlbGVjdCh0b29sLCBpbmRleF90eXBlLCBhbGV2aW5fcmVzb2x1dGlvbiwgYWxldmluX2FsaWdubWVudCwgZ2VuZV9pZCwgc2FtcGxlLCBtZWFuKSAlPiUKICB0aWR5cjo6cGl2b3Rfd2lkZXIoaWRfY29scyA9IGMoZ2VuZV9pZCwgc2FtcGxlKSwKICAgICAgICAgICAgICAgICAgICAgbmFtZXNfZnJvbSA9IGMoInRvb2wiLCAiaW5kZXhfdHlwZSIsICJhbGV2aW5fcmVzb2x1dGlvbiIsICJhbGV2aW5fYWxpZ25tZW50IiksCiAgICAgICAgICAgICAgICAgICAgIHZhbHVlc19mcm9tID0gbWVhbikgJT4lCiAgdGlkeXI6OmRyb3BfbmEoKQpgYGAKCgpOb3cgd2UgY2FuIGxvb2sgYXQgdGhlIGNvcnJlbGF0aW9uIG9mIG1lYW4gZ2VuZSBleHByZXNzaW9uIGFjcm9zcyBlYWNoIG9mIG91ciB0b29sIGNvbmZpZ3VyYXRpb25zIGluIGVhY2ggc2FtcGxlLiAKCmBgYHtyfQpjZWxsX3Jvd2RhdGFfY29yICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoc2FtcGxlKSAlPiUKICBkcGx5cjo6c3VtbWFyaXplKAogICAgY0ROQV9zYWxpZ25fZnVsbF9jb3IgPSBjb3IoYGNlbGxyYW5nZXJfY0ROQV9ub3RfYWxldmluX25vdF9hbGV2aW5gLCBgYWxldmluLWZyeV9jRE5BX2Z1bGxfc2FsaWduYCwgbWV0aG9kID0gInNwZWFybWFuIiksCiAgICBjRE5BX3NrZXRjaF9mdWxsX2NvciA9Y29yKGBjZWxscmFuZ2VyX2NETkFfbm90X2FsZXZpbl9ub3RfYWxldmluYCwgYGFsZXZpbi1mcnlfY0ROQV9mdWxsX3NrZXRjaGAsIG1ldGhvZCA9ICJzcGVhcm1hbiIpLAogICAgY0ROQV9zYWxpZ25fY3JfY29yID0gY29yKGBjZWxscmFuZ2VyX2NETkFfbm90X2FsZXZpbl9ub3RfYWxldmluYCwgYGFsZXZpbi1mcnlfY0ROQV9jcl9zYWxpZ25gLCBtZXRob2QgPSAic3BlYXJtYW4iKSwKICAgIGNETkFfc2tldGNoX2NyX2NvciA9IGNvcihgY2VsbHJhbmdlcl9jRE5BX25vdF9hbGV2aW5fbm90X2FsZXZpbmAsIGBhbGV2aW4tZnJ5X2NETkFfY3Jfc2tldGNoYCwgbWV0aG9kID0gInNwZWFybWFuIiksIAogICAgc3BsaWNpX3NhbGlnbl9mdWxsX2NvciA9IGNvcihgY2VsbHJhbmdlcl9jRE5BX25vdF9hbGV2aW5fbm90X2FsZXZpbmAsIGBhbGV2aW4tZnJ5X3NwbGljaV9mdWxsX3NhbGlnbmAsIG1ldGhvZCA9ICJzcGVhcm1hbiIpLAogICAgc3BsaWNpX3NrZXRjaF9mdWxsX2NvciA9IGNvcihgY2VsbHJhbmdlcl9jRE5BX25vdF9hbGV2aW5fbm90X2FsZXZpbmAsIGBhbGV2aW4tZnJ5X3NwbGljaV9mdWxsX3NrZXRjaGAsIG1ldGhvZCA9ICJzcGVhcm1hbiIpLAogICAgc3BsaWNpX3NhbGlnbl9jcl9jb3IgPSBjb3IoYGNlbGxyYW5nZXJfY0ROQV9ub3RfYWxldmluX25vdF9hbGV2aW5gLCBgYWxldmluLWZyeV9zcGxpY2lfY3Jfc2FsaWduYCwgbWV0aG9kID0gInNwZWFybWFuIiksCiAgICBzcGxpY2lfc2tldGNoX2NyX2NvciA9IGNvcihgY2VsbHJhbmdlcl9jRE5BX25vdF9hbGV2aW5fbm90X2FsZXZpbmAsIGBhbGV2aW4tZnJ5X3NwbGljaV9jcl9za2V0Y2hgLCBtZXRob2QgPSAic3BlYXJtYW4iKQogICkKYGBgCgpgYGB7cn0KbnVjbGV1c19yb3dkYXRhX2NvciAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KHNhbXBsZSkgJT4lCiAgZHBseXI6OnN1bW1hcml6ZSgKICAgIHNwbGljaV9zYWxpZ25fZnVsbF9jb3IgPSBjb3IoYGNlbGxyYW5nZXJfc3BsaWNpX25vdF9hbGV2aW5fbm90X2FsZXZpbmAsIGBhbGV2aW4tZnJ5X3NwbGljaV9mdWxsX3NhbGlnbmAsIG1ldGhvZCA9ICJzcGVhcm1hbiIpLAogICAgc3BsaWNpX3NrZXRjaF9mdWxsX2NvciA9IGNvcihgY2VsbHJhbmdlcl9zcGxpY2lfbm90X2FsZXZpbl9ub3RfYWxldmluYCwgYGFsZXZpbi1mcnlfc3BsaWNpX2Z1bGxfc2tldGNoYCwgbWV0aG9kID0gInNwZWFybWFuIiksCiAgICBzcGxpY2lfc2FsaWduX2NyX2NvciA9IGNvcihgY2VsbHJhbmdlcl9zcGxpY2lfbm90X2FsZXZpbl9ub3RfYWxldmluYCwgYGFsZXZpbi1mcnlfc3BsaWNpX2NyX3NhbGlnbmAsIG1ldGhvZCA9ICJzcGVhcm1hbiIpLAogICAgc3BsaWNpX3NrZXRjaF9jcl9jb3IgPSBjb3IoYGNlbGxyYW5nZXJfc3BsaWNpX25vdF9hbGV2aW5fbm90X2FsZXZpbmAsIGBhbGV2aW4tZnJ5X3NwbGljaV9jcl9za2V0Y2hgLCBtZXRob2QgPSAic3BlYXJtYW4iKQogICkKYGBgCkp1c3QgbGlrZSB3aXRoIFVNSXMvY2VsbCwgdGhlc2UgY29ycmVsYXRpb25zIGFyZSBxdWl0ZSBoaWdoIGFjcm9zcyBhbGwgb2YgdGhlIHRvb2xzIGFuZCBhbGwgb2YgdGhlIHNhbXBsZXMuIApUaGUgYmlnZ2VzdCBkcm9wIGluIGNvcnJlbGF0aW9uIGRvZXMgc2VlbSB0byBiZSBpbiB0aGUgYHNwbGljaV9zYWxpZ25fZnVsbGAgYW5kIGBzcGxpY2lfc2tldGNoX2Z1bGxgIHZzLiBgY2VsbHJhbmdlcmAgY29tcGFyaXNvbnMgaW4gdGhlIHNpbmdsZS1jZWxsIHNhbXBsZXMgd2l0aCBvbmx5IGFyb3VuZCAwLjk1LTAuOTcgaW4gY29ycmVsYXRpb24gY29lZmZpY2llbnRzLiAKCldlIGNhbiBsb29rIGF0IGEgZmV3IGV4YW1wbGVzIG1vcmUgY2xvc2VseSB0byBzZWUgaG93IHdlbGwgdGhlIG1lYW4gZ2VuZSBleHByZXNzaW9uIGZvciBlYWNoIGdlbmUgYWN0dWFsbHkgbGluZXMgdXAuIAoKYGBge3J9CmdncGxvdChjZWxsX3Jvd2RhdGFfY29yLCBhZXMoeCA9IGBjZWxscmFuZ2VyX2NETkFfbm90X2FsZXZpbl9ub3RfYWxldmluYCwgeSA9IGBhbGV2aW4tZnJ5X2NETkFfZnVsbF9za2V0Y2hgKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDAuNSwgYWxwaGEgPSAwLjEpICsgCiAgZmFjZXRfd3JhcCh+IHNhbXBsZSkgKyAKICBzY2FsZV94X2xvZzEwKCkgKyAKICBzY2FsZV95X2xvZzEwKCkgKyAKICBsYWJzKHggPSAiQ2VsbCBSYW5nZXIgbWVhbiBnZW5lIGV4cHJlc3Npb24iLCB5ID0gIkFsZXZpbiBGcnksIGNETkEgaW5kZXgsIEZ1bGwgUmVzb2x1dGlvbiBTa2V0Y2ggTWVhbiBnZW5lIGV4cHJlc3Npb24iKSArIAogIHRoZW1lX2NsYXNzaWMoKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoY2VsbF9yb3dkYXRhX2NvciwgYWVzKHggPSBgY2VsbHJhbmdlcl9jRE5BX25vdF9hbGV2aW5fbm90X2FsZXZpbmAsIHkgPSBgYWxldmluLWZyeV9jRE5BX2NyX3NrZXRjaGApKSArCiAgZ2VvbV9wb2ludChzaXplID0gMC41LCBhbHBoYSA9IDAuMSkgKyAKICBmYWNldF93cmFwKH4gc2FtcGxlKSArIAogIHNjYWxlX3hfbG9nMTAoKSArIAogIHNjYWxlX3lfbG9nMTAoKSArIAogIGxhYnMoeCA9ICJDZWxsIFJhbmdlciBtZWFuIGdlbmUgZXhwcmVzc2lvbiIsIHkgPSAiQWxldmluIEZyeSwgY0ROQSBpbmRleCwgY3ItbGlrZSBSZXNvbHV0aW9uIFNrZXRjaCBNZWFuIGdlbmUgZXhwcmVzc2lvbiIpICsgCiAgdGhlbWVfY2xhc3NpYygpCmBgYAoKYGBge3J9CmdncGxvdChjZWxsX3Jvd2RhdGFfY29yLCBhZXMoeCA9IGBjZWxscmFuZ2VyX2NETkFfbm90X2FsZXZpbl9ub3RfYWxldmluYCwgeSA9IGBhbGV2aW4tZnJ5X3NwbGljaV9mdWxsX3NrZXRjaGApKSArCiAgZ2VvbV9wb2ludChzaXplID0gMC41LCBhbHBoYSA9IDAuMSkgKyAKICBmYWNldF93cmFwKH4gc2FtcGxlKSArIAogIHNjYWxlX3hfbG9nMTAoKSArIAogIHNjYWxlX3lfbG9nMTAoKSArIAogIGxhYnMoeCA9ICJDZWxsIFJhbmdlciBtZWFuIGdlbmUgZXhwcmVzc2lvbiIsIHkgPSAiQWxldmluIEZyeSwgc3BsaWNpIGluZGV4LCBGdWxsIFJlc29sdXRpb24gU2tldGNoIE1lYW4gZ2VuZSBleHByZXNzaW9uIikgKyAKICB0aGVtZV9jbGFzc2ljKCkKYGBgCgpgYGB7cn0KZ2dwbG90KGNlbGxfcm93ZGF0YV9jb3IsIGFlcyh4ID0gYGNlbGxyYW5nZXJfY0ROQV9ub3RfYWxldmluX25vdF9hbGV2aW5gLCB5ID0gYGFsZXZpbi1mcnlfc3BsaWNpX2NyX3NrZXRjaGApKSArCiAgZ2VvbV9wb2ludChzaXplID0gMC41LCBhbHBoYSA9IDAuMSkgKyAKICBmYWNldF93cmFwKH4gc2FtcGxlKSArIAogIHNjYWxlX3hfbG9nMTAoKSArIAogIHNjYWxlX3lfbG9nMTAoKSArIAogIGxhYnMoeCA9ICJDZWxsIFJhbmdlciBtZWFuIGdlbmUgZXhwcmVzc2lvbiIsIHkgPSAiQWxldmluIEZyeSwgc3BsaWNpIGluZGV4LCBjci1saWtlIFJlc29sdXRpb24gU2tldGNoIE1lYW4gZ2VuZSBleHByZXNzaW9uIikgKyAKICB0aGVtZV9jbGFzc2ljKCkKYGBgCkludGVyZXN0aW5nbHksIHdoZW4geW91IGxvb2sgYXQgdGhlIGNETkEgaW5kZXgsIHlvdSBkbyBzZWUgZ2VuZXMgd2l0aCBoaWdoZXIgZXhwcmVzc2lvbiBmb3VuZCBpbiBBbGV2aW4tZnJ5IGluIGNvbXBhcmVkIHRvIGNlbGxyYW5nZXIgKHNob3duIGJ5IGEgZ3JvdXAgdG8gdGhlIHVwcGVyIGxlZnQgb2YgdGhlIGRpYWdvbmFsKSwgYnV0IHdoZW4geW91IG1vdmUgdG8gdGhlIHNwbGljaSBpbmRleCB0aGF0IGdyb3VwIGRpc2FwcGVhcnMuIApXaXRoIHRoZSBzcGxpY2kgaW5kZXggYW5kIGZ1bGwgcmVzb2x1dGlvbiB0aGVyZSBzdGlsbCBhcHBlYXJzIHRvIGJlIHNvbWUgaW5jcmVhc2UgaW4gZ2VuZSBleHByZXNzaW9uIGRldGVjdGVkIGluIEFsZXZpbi1mcnksIGJ1dCB3aXRoIHRoZSBjci1saWtlIHJlc29sdXRpb24gaXQgYWxtb3N0IGxvb2tzIGxpa2UgdGhlcmUgaXMgbm93IGxvd2VyIGRldGVjdGlvbiBpbiBnZW5lIGV4cHJlc3Npb24gYW5kIHNvbWUgZ2VuZXMgaGF2ZSBsb3N0IGdlbmUgZXhwcmVzc2lvbiAoc2hvd24gd2l0aCBhIGdyb3VwIHRvIHRoZSByaWdodCBvZiB0aGUgZGlhZ29uYWwgaW4gdGhlIGxhc3QgcGxvdCkuIAoKTGV0J3MgbG9vayBhdCB0aGUgc2luZ2xlLW51Y2xlaSBkYXRhLiAKCmBgYHtyfQpnZ3Bsb3QobnVjbGV1c19yb3dkYXRhX2NvciwgYWVzKHggPSBgY2VsbHJhbmdlcl9zcGxpY2lfbm90X2FsZXZpbl9ub3RfYWxldmluYCwgeSA9IGBhbGV2aW4tZnJ5X3NwbGljaV9mdWxsX3NrZXRjaGApKSArCiAgZ2VvbV9wb2ludChzaXplID0gMC41LCBhbHBoYSA9IDAuMSkgKyAKICBmYWNldF93cmFwKH4gc2FtcGxlKSArIAogIHNjYWxlX3hfbG9nMTAoKSArIAogIHNjYWxlX3lfbG9nMTAoKSArIAogIGxhYnMoeCA9ICJDZWxsIFJhbmdlciBtZWFuIGdlbmUgZXhwcmVzc2lvbiIsIHkgPSAiQWxldmluIEZyeSwgc3BsaWNpIGluZGV4LCBGdWxsIFJlc29sdXRpb24gU2tldGNoIE1lYW4gZ2VuZSBleHByZXNzaW9uIikgKyAKICB0aGVtZV9jbGFzc2ljKCkKYGBgCmBgYHtyfQpnZ3Bsb3QobnVjbGV1c19yb3dkYXRhX2NvciwgYWVzKHggPSBgY2VsbHJhbmdlcl9zcGxpY2lfbm90X2FsZXZpbl9ub3RfYWxldmluYCwgeSA9IGBhbGV2aW4tZnJ5X3NwbGljaV9jcl9za2V0Y2hgKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDAuNSwgYWxwaGEgPSAwLjEpICsgCiAgZmFjZXRfd3JhcCh+IHNhbXBsZSkgKyAKICBzY2FsZV94X2xvZzEwKCkgKyAKICBzY2FsZV95X2xvZzEwKCkgKyAKICBsYWJzKHggPSAiQ2VsbCBSYW5nZXIgbWVhbiBnZW5lIGV4cHJlc3Npb24iLCB5ID0gIkFsZXZpbiBGcnksIHNwbGljaSBpbmRleCwgY3ItbGlrZSBSZXNvbHV0aW9uIFNrZXRjaCBNZWFuIGdlbmUgZXhwcmVzc2lvbiIpICsgCiAgdGhlbWVfY2xhc3NpYygpCmBgYApIZXJlLCB3ZSBzZWUgYSBzaW1pbGFyIHRyZW5kIHdoZXJlIHdpdGggdGhlIGZ1bGwgcmVzb2x1dGlvbiB0aGVyZSBhcmUgZ2VuZXMgd2l0aCBpbmNyZWFzZWQgZXhwcmVzc2lvbiBpbiBBbGV2aW4tZnJ5LCB3aGlsZSB3aXRoIHRoZSBjci1saWtlIGl0IHNlZW1zIG1vcmUgY2VudGVyZWQgYXJvdW5kIHRoZSBkaWFnb25hbCB3aXRoIHNvbWUgZ2VuZXMgaGF2aW5nIGluY3JlYXNlZCBleHByZXNzaW9uIGFuZCBzb21lIGhhdmluZyBsb3dlciBleHByZXNzaW9uIHRoYW4gaW4gY2VsbHJhbmdlci4gCgoqU29tZSBjbG9zaW5nIHRob3VnaHRzOioKClNob3VsZCB3ZSBiZSBleHBsb3Jpbmcgc29tZSBkaWZmZXJlbnQgZmlsdGVyaW5nIG9wdGlvbnMgd2l0aCBBbGV2aW4tZnJ5PyAKSXQgbG9va3MgbGlrZSBBbGV2aW4tZnJ5IGlzIGRvaW5nIGp1c3QgYXMgZ29vZCBvZiBhIGpvYiBhcyBjZWxscmFuZ2VyLCBidXQgc3BlY2lmaWNhbGx5IHdpdGggdGhlIHNpbmdsZS1udWNsZWkgZGF0YSwgd2Ugc2VlIG1vcmUgY2VsbHMgd2l0aCBsb3cgY291bnRzIHRoYXQgYXJlIGluIHRoZSBmaW5hbCBjb3VudHMgbWF0cml4IC0gbWF5YmUgdGhpcyBpc24ndCBhIHByb2JsZW0gYW5kIGxvdyBjb3VudCBjZWxscyB3b3VsZCBnZXQgcmVtb3ZlZCBpZGVhbGx5IGJlZm9yZSBhbnkgZG93bnN0cmVhbSBhbmFseXNpcyBhbnl3YXlzLiAKClNwbGljaSB3aXRoIHNpbmdsZS1jZWxsIHNhbXBsZXMgc2VlbXMgdG8gYmUgcGVyZm9ybWluZyBzaW1pbGFybHkgdG8gdGhlIG90aGVyIEFsZXZpbi1mcnkgbW9kZXMgYW5kIGNlbGxyYW5nZXIgLSBpdCBldmVuIGxvb2tzIGxpa2UgaXQgZG9lcyBkZWNyZWFzZSBzb21lIGdlbmUgZXhwcmVzc2lvbiBpbiBnZW5lcyB0aGF0IGFyZSBwb29ybHkgY29ycmVsYXRlZCBiZXR3ZWVuIEFsZXZpbi1mcnkgYW5kIGNlbGxyYW5nZXIuIAoKQ3ItbGlrZSBnaXZlcyBzaW1pbGFyIHJlc3VsdHMgdG8gY2VsbHJhbmdlciwgbW9yZSBzbyB0aGFuIHRoZSBmdWxsIHJlc29sdXRpb24sIGFsdGhvdWdoIGl0IGRvZXMgbG9vayBsaWtlIHdlIGNvdWxkIGJlIGxlYWRpbmcgdG8gc29tZSBsb3dlciBnZW5lIGV4cHJlc3Npb24gaW4gdGhlIHNpbmdsZS1jZWxsIHNhbXBsZXMgd2l0aCBBbGV2aW4tZnJ5LiAKSSdtIG5vdCBzdXJlIEkgY2FuIGNvbmZpZGVudGx5IG1ha2UgYSBkZWNpc2lvbiBoZXJlIG9uIHdoaWNoIG9uZSB3b3VsZCBiZSB0aGUgYXBwcm9wcmlhdGUgY2hvaWNlIHlldCAoYWx0aG91Z2ggSSBjYW4gc2F5IEkgdGhpbmsgYm90aCBzZWVtIHRvIGRvIHdlbGwpLgo=